[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntab_width = 4\nindent_style = space\ntrim_trailing_whitespace = true\n\n[*.xml]\nindent_size = 4\n\n[*.{yml,yaml}]\nindent_size = 2\n\n[*.{md,mkd,markdown}]\nindent_size = 4\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"maven\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"daily\"\n    open-pull-requests-limit: 32\n"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "content": "# Quickstart for GitHub Actions\n# https://docs.github.com/en/actions/quickstart\n\nname: Fast CI\non: [ push, pull_request, workflow_dispatch ]\njobs:\n  test:\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 10\n    strategy:\n      matrix:\n        os: [ ubuntu-latest, windows-latest ]\n        java: [ 17 ]\n      fail-fast: false\n      max-parallel: 64\n    name: fast test on Java ${{ matrix.java }} OS ${{ matrix.os }}\n\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-java@v3\n        with:\n          java-version: ${{ matrix.java }}\n          distribution: zulu\n          cache: maven\n\n      - name: Build with Maven\n        run: ./mvnw -V --no-transfer-progress clean install\n\n      - name: remove cola self maven install files for OS *nix\n        run: rm -rf $HOME/.m2/repository/com/alibaba/{cola,craftsman}\n        # https://docs.github.com/en/actions/learn-github-actions/variables#detecting-the-operating-system\n        # https://docs.github.com/en/actions/learn-github-actions/expressions\n        if: runner.os != 'Windows'\n      - name: remove cola self maven install files for OS Windows\n        run: |\n          Remove-Item -Recurse -Force $home/.m2/repository/com/alibaba/cola\n          Remove-Item -Recurse -Force $home/.m2/repository/com/alibaba/craftsman\n        if: runner.os == 'Windows'\n\n"
  },
  {
    "path": ".github/workflows/ci_by_multiply_java_versions.yaml",
    "content": "# Quickstart for GitHub Actions\n# https://docs.github.com/en/actions/quickstart\n\nname: Strong CI with multiply java versions\non: [ push, pull_request, workflow_dispatch ]\njobs:\n  test:\n    # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#choosing-github-hosted-runners\n    runs-on: ubuntu-latest\n    timeout-minutes: 20\n    name: test by multiply java versions\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          submodules: recursive\n\n      - name: Setup Java\n        uses: actions/setup-java@v4\n        with:\n          # https://github.com/actions/setup-java?tab=readme-ov-file#install-multiple-jdks\n          java-version: |\n            17\n            21\n            22\n          distribution: zulu\n          cache: maven\n\n      - run: scripts/integration_test\n        env:\n          JAVA17_HOME: ${{ env.JAVA_HOME_17_X64 }}\n          JAVA21_HOME: ${{ env.JAVA_HOME_21_X64 }}\n          JAVA22_HOME: ${{ env.JAVA_HOME_22_X64 }}\n\n      - name: remove cola self maven install files\n        run: rm -rf $HOME/.m2/repository/com/alibaba/{cola,craftsman}\n"
  },
  {
    "path": ".gitignore",
    "content": "target/\n!.mvn/wrapper/maven-wrapper.jar\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\n\n### BUILD ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\ntarget/\nout/\n.DS_Store\n.vscode/settings.json\n${project.build.directory}\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"scripts/bash-buddy\"]\n\tpath = scripts/bash-buddy\n\turl = https://github.com/foldright/bash-buddy.git\n"
  },
  {
    "path": ".mvn/wrapper/maven-wrapper.properties",
    "content": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\nwrapperVersion=3.3.2\ndistributionType=only-script\ndistributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip\n"
  },
  {
    "path": "LICENSE",
    "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\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\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\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\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\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\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\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\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\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\n    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\n  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": "# 🥤 COLA v5\n\n[![Fast CI](https://img.shields.io/github/actions/workflow/status/alibaba/cola/ci.yaml?branch=master&logo=github&logoColor=white&label=fast%20ci)](https://github.com/alibaba/cola/actions/workflows/ci.yaml)\n[![Multiply Java versions CI](https://img.shields.io/github/actions/workflow/status/alibaba/cola/ci_by_multiply_java_versions.yaml?branch=master&logo=github&logoColor=white&label=strong%20ci)](https://github.com/alibaba/cola/actions/workflows/ci_by_multiply_java_versions.yaml)\n[![License](https://img.shields.io/badge/license-LGPL--2.1-4EB1BA.svg?color=4D7A97&logo=apache)](LICENSE)\n[![Java support](https://img.shields.io/badge/Java-17+-339933?logo=OpenJDK&logoColor=white)](https://openjdk.java.net/)\n[![Maven Central](https://img.shields.io/maven-central/v/com.alibaba.cola/cola-component-dto.svg?logo=apache-maven&label=maven%20central)](https://central.sonatype.com/namespace/com.alibaba.cola)\n[![GitHub Releases](https://img.shields.io/github/release/alibaba/COLA.svg)](https://github.com/alibaba/COLA/releases)\n[![GitHub Stars](https://img.shields.io/github/stars/alibaba/COLA?style=flat)](https://github.com/alibaba/COLA/stargazers)\n[![GitHub Forks](https://img.shields.io/github/forks/alibaba/COLA?style=flat)](https://github.com/alibaba/COLA/fork)\n[![user repos](https://badgen.net/github/dependents-repo/alibaba/COLA?label=user%20repos)](https://github.com/alibaba/COLA/network/dependents)\n[![GitHub issues](https://img.shields.io/github/issues/alibaba/COLA.svg)](https://github.com/alibaba/COLA/issues)\n[![GitHub Contributors](https://img.shields.io/github/contributors/alibaba/COLA)](https://github.com/alibaba/COLA/graphs/contributors)\n[![gitpod: Ready to Code](https://img.shields.io/badge/Gitpod-ready%20to%20code-339933?label=gitpod&logo=gitpod&logoColor=white)](https://gitpod.io/#https://github.com/alibaba/COLA)\n\n<strong>COLA 是 Clean Object-Oriented and Layered Architecture的缩写，代表“整洁面向对象分层架构”。\n目前COLA已经发展到[COLA v5](#版本迭代)。</strong>\n\n> - 想了解更多COLA信息，请关注微信公众号：  \n> <a href=\"#dummy\"><img src=\"https://img-blog.csdnimg.cn/2020110314110321.png\" width=\"25%\" alt=\"qrcode\" /></a>\n> - 想了解更多COLA背后的故事，请支持我的新书[《程序员的底层思维》](https://item.jd.com/13652002.html)\n\nCOLA分为两个部分，COLA架构和COLA组件。\n\n# 一、COLA架构\n\n## COLA 概述\n\n**架构**的**意义** 就是 要素结构：\n\n- 要素 是 组成架构的重要元素；\n- 结构 是 要素之间的关系。\n\n而 **应用架构**的**意义** 就在于\n\n- 定义一套良好的结构；\n- 治理应用复杂度，降低系统熵值；\n- 从随心所欲的混乱状态，走向井井有条的有序状态。\n\n<a href=\"#dummy\"><img src=\"https://img-blog.csdnimg.cn/e27c22d706084ead900c8838326135f3.png\" alt=\"arch why\" /></a>\n\nCOLA架构就是为此而生，其核心职责就是定义良好的应用结构，提供最佳应用架构的最佳实践。通过不断探索，我们发现良好的分层结构，良好的包结构定义，可以帮助我们治理混乱不堪的业务应用系统。\n\n<a href=\"#dummy\"><img src=\"https://img-blog.csdnimg.cn/2020120918285068.png\" alt=\"cure\" /></a>\n\n经过多次迭代，我们定义出了相对稳定、可靠的应用架构：\n\n<a href=\"#dummy\"><img src=\"https://img-blog.csdnimg.cn/6549230c6723448fb3ab51ca74829e80.png\" alt=\"cola arch\" /></a>\n\n## COLA Archetypes\n\n好的应用架构，都遵循一些共同模式，不管是六边形架构、洋葱圈架构、整洁架构、还是COLA架构，**都提倡以业务为核心，解耦外部依赖，分离业务复杂度和技术复杂度等**。\n\nCOLA架构区别于这些架构的地方，在于除了思想之外，我们还提供了可落地的工具和实践指导。\n\n为了能够快速创建满足COLA架构的应用，我们提供了两个`archetype`，位于[`cola-archetypes`目录](cola-archetypes)下：\n\n1. `cola-archetype-service`：用来创建纯后端服务的`archetype`。\n2. `cola-archetype-web`：用来创建`adapter`和后端服务一体的`web`应用`archetype`。\n\n# 二、COLA组件\n\n此外，我们还提供了一些非常有用的通用组件，这些组件可以帮助我们提升研发效率。\n\n这些功能组件被收拢在[`cola-components`目录](cola-components)下面。到目前为止，我们已经沉淀了以下组件：\n\n组件名称 | 功能 | 依赖\n------ | ---- | ----\n`cola-component-dto` | 定义了`DTO`格式，包括分页 |无\n`cola-component-exception` | 定义了异常格式，<br>主要有`BizException`和`SysException` |无\n`cola-component-statemachine` | 状态机组件 | 无\n`cola-component-domain-starter` | `Spring`托管的领域实体组件 | 无\n`cola-component-catchlog-starter` | 异常处理和日志组件 | `exception`、`dto`组件\n`cola-component-extension-starter` | 扩展点组件 | 无\n`cola-component-test-container` | 测试容器组件 | 无\n\n# 三、如何使用COLA\n\n## 1. 创建应用\n\n执行以下命令：\n\n```bash\nmvn archetype:generate \\\n    -DgroupId=com.alibaba.cola.demo.web \\\n    -DartifactId=demo-web \\\n    -Dversion=1.0.0-SNAPSHOT \\\n    -Dpackage=com.alibaba.demo \\\n    -DarchetypeArtifactId=cola-framework-archetype-web \\\n    -DarchetypeGroupId=com.alibaba.cola \\\n    -DarchetypeVersion=5.0.0\n```\n\n命令执行成功的话，会看到如下的应用代码结构：\n\n<a href=\"#dummy\"><img src=\"https://img-blog.csdnimg.cn/20201209192258840.png\" alt=\"demo struture\" /></a>\n\n## 2. 运行应用\n\n- 在`项目`目录下运行`mvn install`（如果不想运行测试，可以加上`-DskipTests`参数）。\n- 进入`start`目录，执行`mvn spring-boot:run`。  \n  运行成功的话，可以看到`SpringBoot`启动成功的界面。\n- 生成的应用中，已经实现了一个简单的`Rest`请求，可以在浏览器中输入 http://localhost:8080/helloworld 进行测试。\n\n如果要生成不是`web`工程而是`service`工程也类似，执行的是下面的命令：\n\n```bash\nmvn archetype:generate \\\n    -DgroupId=com.alibaba.cola.demo.service \\\n    -DartifactId=demo-service \\\n    -Dversion=1.0.0-SNAPSHOT \\\n    -Dpackage=com.alibaba.demo \\\n    -DarchetypeArtifactId=cola-framework-archetype-service \\\n    -DarchetypeGroupId=com.alibaba.cola \\\n    -DarchetypeVersion=5.0.0\n```\n\n# 版本迭代\n\n## 5.0.0 版本\n1. 支持jdk17和SpringBoot 3.x\n2. 增加cola-archetype-light，支持新的基于package轻量级分层架构\n3. 增加cola-component-unittest组件，支持[新的单元测试](https://blog.csdn.net/significantfrank/article/details/137495244)\n4. 增强cola-component-test-container组件，支持Junit5的Extension\n\n\n## 4.0.0 版本\n\nhttps://blog.csdn.net/significantfrank/article/details/110934799\n\n## 3.1.0 版本\n\nhttps://blog.csdn.net/significantfrank/article/details/109529311\n\n1. 进一步简化了`cola-core`，只保留了扩展能力。\n2. 将`exception`从`cola-core`移入到`cola-common`。\n3. 对`archetype`中的分包逻辑进行重构，改成按照`domain`做划分。\n4. 将`cola-archetype-web`中的`controller`改名为`adapter`，为了呼应六边形架构的命名。\n\n## 3.0.0 版本\n\nhttps://blog.csdn.net/significantfrank/article/details/106976804\n\n## 2.0.0 版本\n\nhttps://blog.csdn.net/significantfrank/article/details/100074716\n\n## 1.0.0 版本\n\nhttps://blog.csdn.net/significantfrank/article/details/85785565\n\n\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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    <parent>\n        <groupId>com.alibaba.cola</groupId>\n        <artifactId>cola-framework-archetypes-parent</artifactId>\n        <version>5.x-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>cola-archetype-light</artifactId>\n    <packaging>maven-archetype</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n    </developers>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/META-INF/maven/archetype-metadata.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archetype-descriptor xsi:schemaLocation=\"https://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0 http://maven.apache.org/xsd/archetype-descriptor-1.1.0.xsd\" name=\"charging-system\"\n    xmlns=\"https://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n  <fileSets>\n    <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n      <directory>src/main/java</directory>\n      <includes>\n        <include>**/*.java</include>\n      </includes>\n    </fileSet>\n    <fileSet filtered=\"true\" encoding=\"UTF-8\">\n      <directory>src/main/resources</directory>\n      <includes>\n        <include>**/*.xml</include>\n      </includes>\n    </fileSet>\n    <fileSet encoding=\"UTF-8\">\n      <directory>src/main/resources</directory>\n      <includes>\n        <include>**/*.yml</include>\n      </includes>\n    </fileSet>\n    <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n      <directory>src/test/java</directory>\n      <includes>\n        <include>**/*.java</include>\n      </includes>\n    </fileSet>\n    <fileSet filtered=\"true\" encoding=\"UTF-8\">\n      <directory>src/test/resources</directory>\n      <includes>\n        <include>**/*.xml</include>\n      </includes>\n    </fileSet>\n    <fileSet encoding=\"UTF-8\">\n      <directory>src/test</directory>\n      <includes>\n        <include>**/*.http</include>\n      </includes>\n    </fileSet>\n    <fileSet encoding=\"UTF-8\">\n      <directory>src/test/resources</directory>\n      <includes>\n        <include>**/*.json</include>\n        <include>**/*.yml</include>\n      </includes>\n    </fileSet>\n    <fileSet filtered=\"true\" encoding=\"UTF-8\">\n      <directory>.idea</directory>\n      <includes>\n        <include>**/*.xml</include>\n      </includes>\n    </fileSet>\n    <fileSet encoding=\"UTF-8\">\n      <directory>.idea</directory>\n      <includes>\n        <include>**/*.gitignore</include>\n      </includes>\n    </fileSet>\n    <fileSet encoding=\"UTF-8\">\n      <directory></directory>\n      <includes>\n        <include>img_1.png</include>\n        <include>charge-parent.iml</include>\n        <include>charge.iml</include>\n        <include>img.png</include>\n        <include>charging-system.iml</include>\n        <include>README.md</include>\n      </includes>\n    </fileSet>\n  </fileSets>\n</archetype-descriptor>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/README.md",
    "content": "# 运营商计费系统\n计费系统是一个典型的复杂问题场景，比较适合采用COLA架构，且发挥Domain层的价值。故将其作为COLA的另一个Sample。\n\n运营商计费系统的需求如下：\n\n运营商向用户提供电话服务，支持用户拨打/接听电话，并对通话收取费用。\n如：主动拨打电话收取 0.5 元/分钟的通话费用；接听电话收取 0.4 元/分钟的通话费用。\n\n运营商为了吸引客户，定义了若干电话套餐，总共有三种类型的套餐。我们要设计一个**计费系统**用于套餐计费规则的执行，保存计费记录，并通知**账户系统**扣减费用。\n\n_注意：在一次通话过程中，通话控制系统可能会调用多次计费系统进行计费。_\n\n- 基础套餐\n    1. 主叫收费 0.5 元/分钟\n    2. 被叫收费 0.4 元/分钟\n\n\n- 固定时长套餐\n    1. 套餐月固定费 100 元，包含：200 分钟主叫通话时间+200 分钟被叫接听时间\n    2. 套餐外部分不再参与打折优惠，主叫 0.5 元/分钟，被叫 0.4 元/分钟\n\n\n- 家庭套餐\n    1. 套餐月固定费 20 元\n    2. 用户可以指定 N 个号码作为自己的亲情号\n    3. 用户接听/拨打亲情号均不收费\n    4. 与亲情号之外的号码通话，主叫 0.5 元/分钟，被叫 0.4 元/分钟\n\n# 系统设计\n## 统一语言\n我经常说，建模就是在分析语言，对于任何问题域，熟悉领域知识、理清概念、统一语言都是非常必要且重要的事情。\n对于这个系统也不例外。\n![img_1.png](img_1.png)\n\n## 计费系统和周边系统的关系\n![img.png](img.png)\n\n\n\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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>${groupId}</groupId>\n    <artifactId>${artifactId}</artifactId>\n    <version>${version}</version>\n\n    <properties>\n        <java.version>17</java.version>\n        <maven.compiler.source>17</maven.compiler.source>\n        <maven.compiler.target>17</maven.compiler.target>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\n        <springfox.version>3.0.0</springfox.version>\n        <archunit.version>1.3.0</archunit.version>\n        <spring.boot.version>3.2.0</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-dependencies</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.22</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-jpa</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>8.0.33</version>\n        </dependency>\n        <!-- Unit Test Support start-->\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-test-container</artifactId>\n            <version>4.4.0-SNAPSHOT</version>\n        </dependency>\n        <!--this for embedded database unit test-->\n        <dependency>\n            <groupId>com.h2database</groupId>\n            <artifactId>h2</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <!--this for dependent service mock, to avoid conflict, we'd better use standalone -->\n        <dependency>\n            <groupId>org.wiremock</groupId>\n            <artifactId>wiremock-standalone</artifactId>\n            <version>3.5.4</version>\n            <scope>test</scope>\n        </dependency>\n        <!-- Unit Test Support End-->\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/Application.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package};\r\n\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\n@SpringBootApplication\r\npublic class Application {\r\n\r\n    public static void main(String[] args) {\r\n        SpringApplication.run(Application.class, args);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/adapter/ChargeController.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.adapter;\n\nimport ${package}.application.ChargeServiceI;\nimport ${package}.application.dto.*;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.web.bind.annotation.*;\n\n\n@RestController\n@Slf4j\npublic class ChargeController {\n\n    @Resource\n    private ChargeServiceI chargeService;\n\n    @PostMapping(\"session/{sessionId}/begin\")\n    public Response begin(@PathVariable(name = \"sessionId\") String sessionId,\n                          @RequestParam(\"callingPhoneNo\") String callingPhoneNo,\n                          @RequestParam(\"calledPhoneNo\") String calledPhoneNo) {\n        log.debug(sessionId + \" \" + callingPhoneNo + \" \" + calledPhoneNo);\n        BeginSessionRequest request = new BeginSessionRequest(sessionId, Long.valueOf(callingPhoneNo), Long.valueOf(calledPhoneNo));\n        return chargeService.begin(request);\n    }\n\n    @PostMapping(\"session/{sessionId}/charge\")\n    public Response charge(@PathVariable(name = \"sessionId\") String sessionId,\n                       @RequestParam int duration) {\n        log.debug(sessionId + \" \" + duration);\n        ChargeRequest request = new ChargeRequest(sessionId, duration);\n        return chargeService.charge(request);\n    }\n\n    @PostMapping(\"session/{sessionId}/end\")\n    public Response end(@PathVariable(name = \"sessionId\") String sessionId,\n                    @RequestParam int duration) {\n        log.debug(sessionId + \" \" + duration);\n        EndSessionRequest request = new EndSessionRequest(sessionId, duration);\n        return chargeService.end(request);\n    }\n\n    @GetMapping(\"{sessionId}/chargeRecords\")\n    public MultiResponse<ChargeRecordDto> getChargeRecord(@PathVariable(name = \"sessionId\") String sessionId) {\n        return chargeService.listChargeRecords(sessionId);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/application/ChargeServiceI.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.application;\r\n\r\nimport ${package}.application.dto.*;\r\n\r\npublic interface ChargeServiceI {\r\n    Response begin(BeginSessionRequest request);\r\n\r\n    Response charge(ChargeRequest request);\r\n\r\n    Response end(EndSessionRequest request);\r\n\r\n    MultiResponse<ChargeRecordDto> listChargeRecords(String sessionId);\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/application/ChargeServiceImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.application;\n\nimport ${package}.application.dto.*;\nimport ${package}.domain.account.Account;\nimport ${package}.domain.account.AccountDomainService;\nimport ${package}.domain.charge.CallType;\nimport ${package}.domain.charge.ChargeContext;\nimport ${package}.domain.charge.ChargeRecord;\nimport ${package}.domain.charge.Session;\nimport ${package}.domain.gateway.AccountGateway;\nimport ${package}.domain.gateway.ChargeGateway;\nimport ${package}.domain.gateway.SessionGateway;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Service\n@Slf4j\npublic class ChargeServiceImpl implements ChargeServiceI {\n\n    @Resource\n    private SessionGateway sessionGateway;\n\n    @Resource\n    private AccountGateway accountGateway;\n\n    @Resource\n    private AccountDomainService accountDomainService;\n\n    @Resource\n    private ChargeGateway chargeGateway;\n\n    @Override\n    public Response begin(BeginSessionRequest request) {\n        Session session = request.toSession();\n        accountDomainService.canSessionStart(session);\n        sessionGateway.create(session);\n        log.debug(\"Session created successfully :\" + session);\n        return Response.buildSuccess();\n    }\n\n    @Override\n    public Response charge(ChargeRequest request) {\n        log.debug(\"Do charge : \" + request);\n        Session session = sessionGateway.get(request.getSessionId());\n        int durationToCharge = request.getDuration() - session.getChargedDuration();\n        List<ChargeRecord> chargeRecordList = new ArrayList<>();\n        chargeCalling(session, durationToCharge, chargeRecordList);\n        chargeCalled(session, durationToCharge, chargeRecordList);\n        chargeGateway.saveAll(chargeRecordList);\n        session.setChargedDuration(request.getDuration());\n        return Response.buildSuccess();\n    }\n\n    private void chargeCalling(Session session, int durationToCharge, List<ChargeRecord> chargeRecordList) {\n        Account callingAccount = accountGateway.getAccount(session.getCallingPhoneNo());\n        ChargeContext callingCtx = new ChargeContext(CallType.CALLING, session.getCallingPhoneNo(), session.getCalledPhoneNo(), durationToCharge);\n        callingCtx.session = session;\n        callingCtx.account = callingAccount;\n        chargeRecordList.addAll(callingAccount.charge(callingCtx));\n    }\n\n    private void chargeCalled(Session session, int durationToCharge, List<ChargeRecord> chargeRecordList) {\n        Account calledAccount = accountGateway.getAccount(session.getCalledPhoneNo());\n        ChargeContext calledCtx = new ChargeContext(CallType.CALLED, session.getCalledPhoneNo(), session.getCallingPhoneNo(), durationToCharge);\n        calledCtx.session = session;\n        calledCtx.account = calledAccount;\n        chargeRecordList.addAll(calledAccount.charge(calledCtx));\n    }\n\n    @Override\n    public Response end(EndSessionRequest request) {\n        charge(request.toChargeRequest());\n        sessionGateway.end(request.getSessionId());\n        return Response.buildSuccess();\n    }\n\n    @Override\n    public MultiResponse<ChargeRecordDto> listChargeRecords(String sessionId) {\n        List<ChargeRecord> chargeRecordList = chargeGateway.findBySessionId(sessionId);\n        List<ChargeRecordDto> chargeRecordDtoList = new ArrayList<>();\n        for (ChargeRecord chargeRecord : chargeRecordList) {\n            chargeRecordDtoList.add(ChargeRecordDto.fromEntity(chargeRecord));\n        }\n        return MultiResponse.of(chargeRecordDtoList);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/application/dto/BeginSessionRequest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.application.dto;\r\n\r\nimport ${package}.domain.charge.Session;\r\nimport lombok.Data;\r\n\r\n@Data\r\npublic class BeginSessionRequest {\r\n\r\n    /**\r\n     * 本次通话的UUID\r\n     */\r\n    private String sessionId;\r\n\r\n    /**\r\n     * 主叫电话号码\r\n     */\r\n    private long callingPhoneNo;\r\n\r\n    /**\r\n     * 被叫电话号码\r\n     */\r\n    private long calledPhoneNo;\r\n\r\n    public Session toSession(){\r\n        return new Session(sessionId, callingPhoneNo, calledPhoneNo);\r\n    }\r\n\r\n    public BeginSessionRequest() {\r\n    }\r\n\r\n    public BeginSessionRequest(String sessionId, long callingPhoneNo, long calledPhoneNo) {\r\n        this.sessionId = sessionId;\r\n        this.callingPhoneNo = callingPhoneNo;\r\n        this.calledPhoneNo = calledPhoneNo;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/application/dto/ChargeRecordDto.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.application.dto;\r\n\r\nimport ${package}.domain.charge.CallType;\r\nimport ${package}.domain.charge.ChargeRecord;\r\nimport ${package}.domain.charge.chargeplan.ChargePlanType;\r\n\r\npublic class ChargeRecordDto {\r\n    public Long id;\r\n    public String sessionId;\r\n    public long phoneNo;\r\n    public int chargeDuration;\r\n    public long cost;\r\n    public CallType callType;\r\n    public ChargePlanType chargePlanType;\r\n\r\n    public static ChargeRecordDto fromEntity(ChargeRecord chargeRecord){\r\n        ChargeRecordDto dto = new ChargeRecordDto();\r\n        dto.id = chargeRecord.getId();\r\n        dto.sessionId = chargeRecord.getSessionId();\r\n        dto.phoneNo = chargeRecord.getPhoneNo();\r\n        dto.chargeDuration = chargeRecord.getChargeDuration();\r\n        dto.cost = chargeRecord.getCost().getAmount();\r\n        dto.callType = chargeRecord.getCallType();\r\n        dto.chargePlanType = chargeRecord.getChargePlanType();\r\n        return dto;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/application/dto/ChargeRequest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.application.dto;\r\n\r\nimport lombok.Data;\r\n\r\n@Data\r\npublic class ChargeRequest {\r\n\r\n    private String sessionId;\r\n\r\n    /**\r\n     * 当前通话，截止目前的累计时间\r\n     */\r\n    private int duration;\r\n\r\n    public ChargeRequest() {\r\n    }\r\n\r\n    public ChargeRequest(String sessionId, int duration) {\r\n        this.sessionId = sessionId;\r\n        this.duration = duration;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/application/dto/EndSessionRequest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.application.dto;\r\n\r\nimport lombok.Data;\r\n\r\n@Data\r\npublic class EndSessionRequest {\r\n    private String sessionId;\r\n\r\n    /**\r\n     * 当前通话，截止目前的累计时间\r\n     */\r\n    private int duration;\r\n\r\n    public ChargeRequest toChargeRequest() {\r\n        ChargeRequest chargeRequest = new ChargeRequest();\r\n        chargeRequest.setSessionId(sessionId);\r\n        chargeRequest.setDuration(duration);\r\n        return chargeRequest;\r\n    }\r\n\r\n    public EndSessionRequest() {\r\n    }\r\n\r\n    public EndSessionRequest(String sessionId, int duration) {\r\n        this.sessionId = sessionId;\r\n        this.duration = duration;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/application/dto/MultiResponse.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.application.dto;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.Collections;\r\nimport java.util.List;\r\n\r\npublic class MultiResponse<T> extends Response {\r\n\r\n    private static final long serialVersionUID = 1L;\r\n\r\n    private Collection<T> data;\r\n\r\n    public List<T> getData() {\r\n        if (null == data) {\r\n            return Collections.emptyList();\r\n        }\r\n        if (data instanceof List) {\r\n            return (List<T>) data;\r\n        }\r\n        return new ArrayList<>(data);\r\n    }\r\n\r\n    public void setData(Collection<T> data) {\r\n        this.data = data;\r\n    }\r\n\r\n    public boolean isEmpty() {\r\n        return data == null || data.isEmpty();\r\n    }\r\n\r\n    public boolean isNotEmpty() {\r\n        return !isEmpty();\r\n    }\r\n\r\n    public static MultiResponse buildSuccess() {\r\n        MultiResponse response = new MultiResponse();\r\n        response.setSuccess(true);\r\n        return response;\r\n    }\r\n\r\n    public static MultiResponse buildFailure(String errCode, String errMessage) {\r\n        MultiResponse response = new MultiResponse();\r\n        response.setSuccess(false);\r\n        response.setErrCode(errCode);\r\n        response.setErrMessage(errMessage);\r\n        return response;\r\n    }\r\n\r\n    public static <T> MultiResponse<T> of(Collection<T> data) {\r\n        MultiResponse<T> response = new MultiResponse<>();\r\n        response.setSuccess(true);\r\n        response.setData(data);\r\n        return response;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/application/dto/Response.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.application.dto;\r\n\r\npublic class Response {\r\n    private boolean success;\r\n\r\n    private String errCode;\r\n\r\n    private String errMessage;\r\n\r\n    public boolean isSuccess() {\r\n        return success;\r\n    }\r\n\r\n    public void setSuccess(boolean success) {\r\n        this.success = success;\r\n    }\r\n\r\n    public String getErrCode() {\r\n        return errCode;\r\n    }\r\n\r\n    public void setErrCode(String errCode) {\r\n        this.errCode = errCode;\r\n    }\r\n\r\n    public String getErrMessage() {\r\n        return errMessage;\r\n    }\r\n\r\n    public void setErrMessage(String errMessage) {\r\n        this.errMessage = errMessage;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return \"Response [success=\" + success + \", errCode=\" + errCode + \", errMessage=\" + errMessage + \"]\";\r\n    }\r\n\r\n    public static Response buildSuccess() {\r\n        Response response = new Response();\r\n        response.setSuccess(true);\r\n        return response;\r\n    }\r\n\r\n    public static Response buildFailure(String errCode, String errMessage) {\r\n        Response response = new Response();\r\n        response.setSuccess(false);\r\n        response.setErrCode(errCode);\r\n        response.setErrMessage(errMessage);\r\n        return response;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/application/dto/SingleResponse.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.application.dto;\r\n\r\npublic class SingleResponse<T> extends Response {\r\n\r\n    private static final long serialVersionUID = 1L;\r\n\r\n    private T data;\r\n\r\n    public T getData() {\r\n        return data;\r\n    }\r\n\r\n    public void setData(T data) {\r\n        this.data = data;\r\n    }\r\n\r\n    public static SingleResponse buildSuccess() {\r\n        SingleResponse response = new SingleResponse();\r\n        response.setSuccess(true);\r\n        return response;\r\n    }\r\n\r\n    public static SingleResponse buildFailure(String errCode, String errMessage) {\r\n        SingleResponse response = new SingleResponse();\r\n        response.setSuccess(false);\r\n        response.setErrCode(errCode);\r\n        response.setErrMessage(errMessage);\r\n        return response;\r\n    }\r\n\r\n    public static <T> SingleResponse<T> of(T data) {\r\n        SingleResponse<T> response = new SingleResponse<>();\r\n        response.setSuccess(true);\r\n        response.setData(data);\r\n        return response;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/ApplicationContextHelper.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain;\r\n\r\nimport org.springframework.beans.BeansException;\r\nimport org.springframework.context.ApplicationContext;\r\nimport org.springframework.context.ApplicationContextAware;\r\nimport org.springframework.stereotype.Component;\r\n\r\n@Component\r\npublic class ApplicationContextHelper implements ApplicationContextAware {\r\n    private static ApplicationContext applicationContext;\r\n\r\n    @Override\r\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\r\n        ApplicationContextHelper.applicationContext = applicationContext;\r\n    }\r\n\r\n    public static <T> T getBean(Class<T> targetClz) {\r\n        T beanInstance = null;\r\n        //优先按type查\r\n        try {\r\n            beanInstance = (T)applicationContext.getBean(targetClz);\r\n        } catch (Exception e) {\r\n        }\r\n        //按name查\r\n        if (beanInstance == null) {\r\n            String simpleName = targetClz.getSimpleName();\r\n            //首字母小写\r\n            simpleName = Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);\r\n            beanInstance = (T)applicationContext.getBean(simpleName);\r\n        }\r\n        if (beanInstance == null) {\r\n            throw new RuntimeException(\"Component \" + targetClz + \" can not be found in Spring Container\");\r\n        }\r\n        return beanInstance;\r\n    }\r\n\r\n    public static Object getBean(String claz) {\r\n        return ApplicationContextHelper.applicationContext.getBean(claz);\r\n    }\r\n\r\n    public static <T> T getBean(String name, Class<T> requiredType) {\r\n        return ApplicationContextHelper.applicationContext.getBean(name, requiredType);\r\n    }\r\n\r\n    public static <T> T getBean(Class<T> requiredType, Object... params) {\r\n        return ApplicationContextHelper.applicationContext.getBean(requiredType, params);\r\n    }\r\n\r\n    public static ApplicationContext getApplicationContext() {\r\n        return applicationContext;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/BizException.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain;\r\n\r\npublic class BizException extends RuntimeException{\r\n\r\n    public BizException(String errMessage) {\r\n        super(errMessage);\r\n    }\r\n\r\n    public static BizException of(String errMessage){\r\n        return new BizException(errMessage);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/DomainFactory.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain;\r\n\r\npublic class DomainFactory {\r\n\r\n    public static <T> T get(Class<T> entityClz){\r\n        return ApplicationContextHelper.getBean(entityClz);\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/Entity.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain;\r\n\r\nimport org.springframework.beans.factory.config.ConfigurableBeanFactory;\r\nimport org.springframework.context.annotation.Scope;\r\nimport org.springframework.stereotype.Component;\r\n\r\nimport java.lang.annotation.*;\r\n\r\n@Inherited\r\n@Retention(RetentionPolicy.RUNTIME)\r\n@Target({ElementType.TYPE})\r\n@Component\r\n@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)\r\npublic @interface Entity {\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/account/Account.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.account;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport ${package}.domain.BizException;\nimport ${package}.domain.DomainFactory;\nimport ${package}.domain.Entity;\nimport ${package}.domain.charge.*;\nimport ${package}.domain.charge.chargeplan.BasicChargePlan;\nimport ${package}.domain.charge.chargeplan.ChargePlan;\nimport ${package}.domain.charge.chargerule.ChargeRuleFactory;\nimport ${package}.domain.charge.chargerule.CompositeChargeRule;\nimport ${package}.domain.gateway.AccountGateway;\nimport jakarta.annotation.Resource;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\n\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\n@Data\n@Entity\n@Slf4j\npublic class Account {\n    /**\n     * 用户号码\n     */\n    private long phoneNo;\n\n    /**\n     * 账户余额\n     */\n    private Money remaining;\n\n    /**\n     * 账户所拥有的套餐\n     */\n    @JsonIgnore\n    private List<ChargePlan> chargePlanList = new ArrayList<>();;\n\n    @Resource\n    private AccountGateway accountGateway;\n\n    private String name;\n\n    public Account(){\n\n    }\n\n    public Account(long phoneNo, Money amount, List<ChargePlan> chargePlanList){\n        this.phoneNo = phoneNo;\n        this.remaining = amount;\n        this.chargePlanList = chargePlanList;\n    }\n\n    public static Account valueOf(long phoneNo, Money amount) {\n        Account account = DomainFactory.get(Account.class);\n        account.setPhoneNo(phoneNo);\n        account.setRemaining(amount);\n        account.chargePlanList.add(new BasicChargePlan());\n        return account;\n    }\n\n    /**\n     * 检查账户余额是否足够\n     */\n    public void checkRemaining() {\n        if (remaining.isLessThan(Money.of(0))) {\n            throw BizException.of(this.phoneNo + \" has insufficient amount\");\n        }\n    }\n\n    public List<ChargeRecord> charge(ChargeContext ctx) {\n        CompositeChargeRule compositeChargeRule = ChargeRuleFactory.get(chargePlanList);\n        List<ChargeRecord> chargeRecords = compositeChargeRule.doCharge(ctx);\n        log.debug(\"Charges: \"+ chargeRecords);\n\n        //跟新账户系统\n        accountGateway.sync(phoneNo, chargeRecords);\n        return chargeRecords;\n    }\n\n    @Override\n    public String toString() {\n        return \"Account{\" +\n                \"phoneNo=\" + phoneNo +\n                \", remaining=\" + remaining +\n                \", chargePlanList=\" + chargePlanList +\n                \", name=\" + name +\n                '}';\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/account/AccountDomainService.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.account;\n\nimport ${package}.domain.charge.Session;\nimport ${package}.domain.gateway.AccountGateway;\nimport jakarta.annotation.Resource;\nimport org.springframework.stereotype.Component;\n\n\n@Component\npublic class AccountDomainService {\n\n    @Resource\n    private AccountGateway accountGateway;\n\n    public void canSessionStart(Session session){\n        Account callingAccount = accountGateway.getAccount(session.getCallingPhoneNo());\n        Account calledAccount = accountGateway.getAccount(session.getCalledPhoneNo());\n        callingAccount.checkRemaining();\n        calledAccount.checkRemaining();\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/CallType.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge;\r\n\r\npublic enum CallType {\r\n    /**\r\n     * 主叫\r\n     */\r\n    CALLING,\r\n    /**\r\n     * 被叫\r\n     */\r\n    CALLED\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/ChargeContext.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge;\r\n\r\nimport ${package}.domain.account.Account;\r\nimport lombok.Data;\r\n\r\n@Data\r\npublic class ChargeContext {\r\n\r\n    /**\r\n     * 本次通话的Session\r\n     */\r\n    public Session session;\r\n\r\n    /**\r\n     * 呼叫类型\r\n     */\r\n    public CallType callType;\r\n    /**\r\n     * 账号号码\r\n     */\r\n    public long phoneNo;\r\n\r\n    /**\r\n     * 通话另一端号码\r\n     */\r\n    public long otherSidePhoneNo;\r\n\r\n    /**\r\n     * 当前需要被扣费的时长\r\n     */\r\n    public int durationToCharge;\r\n\r\n    /**\r\n     * 被Charge的账号\r\n     */\r\n    public Account account;\r\n\r\n    public ChargeContext(CallType callType, long phoneNo, long otherSidePhoneNo, int durationToCharge) {\r\n        this.callType = callType;\r\n        this.phoneNo = phoneNo;\r\n        this.otherSidePhoneNo = otherSidePhoneNo;\r\n        this.durationToCharge = durationToCharge;\r\n    }\r\n\r\n    public boolean needCharge(){\r\n        return durationToCharge >0;\r\n    }\r\n\r\n    public boolean isCalling(){\r\n        return CallType.CALLING ==  this.callType;\r\n    }\r\n\r\n    public boolean isCalled(){\r\n        return CallType.CALLED == this.callType;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return \"ChargeContext{\" +\r\n                \"callType=\" + callType +\r\n                \", phoneNo=\" + phoneNo +\r\n                \", otherSidePhoneNo=\" + otherSidePhoneNo +\r\n                \", durationToCharge=\" + durationToCharge +\r\n                \", account=\" + account +\r\n                '}';\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/ChargeRecord.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge;\n\nimport ${package}.domain.charge.chargeplan.ChargePlanType;\nimport lombok.Data;\n\nimport jakarta.persistence.*;\nimport java.util.Date;\n\n@Entity\n@Table(name = \"charge_record\")\n@Data\npublic class ChargeRecord {\n\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Long Id;\n\n    private String sessionId;\n\n    private long phoneNo;\n\n    /**\n     * 呼叫类型\n     */\n    @Enumerated(EnumType.STRING)\n    private CallType callType;\n\n    /**\n     * 计费记录所对应的呼叫时长\n     */\n    private int chargeDuration;\n\n    /**\n     * 所属计费套餐\n     */\n    @Enumerated(EnumType.STRING)\n    private ChargePlanType chargePlanType;\n\n    private Money cost;\n\n    @Temporal(TemporalType.TIMESTAMP)\n    public Date createTime;\n\n    @Temporal(TemporalType.TIMESTAMP)\n    public Date updateTime;\n\n    public ChargeRecord() {\n    }\n\n    public ChargeRecord(long phoneNo, CallType callType, int chargeDuration, ChargePlanType chargePlanType, Money cost) {\n        this.phoneNo = phoneNo;\n        this.callType = callType;\n        this.chargeDuration = chargeDuration;\n        this.chargePlanType = chargePlanType;\n        this.cost = cost;\n    }\n\n    @Override\n    public String toString() {\n        return \"Charge{\" +\n                \"phoneNo=\" + phoneNo +\n                \", callType=\" + callType +\n                \", chargeDuration=\" + chargeDuration +\n                \", chargePlanType=\" + chargePlanType +\n                \", cost=\" + cost +\n                '}';\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/Money.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge;\r\n\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.Objects;\r\n\r\n/**\r\n * 这个Money是简化版的，真实场景应该用BigDecimal\r\n */\r\n@Data\r\npublic class Money {\r\n\r\n    /**\r\n     * 单位是角，1代表0.1元， 10代表1元\r\n     */\r\n    private int amount;\r\n\r\n    public Money(int amount) {\r\n        this.amount = amount;\r\n    }\r\n\r\n    public static Money of(int amount){\r\n        return new Money(amount);\r\n    }\r\n\r\n    public boolean isLessThan(Money money){\r\n        return this.amount <= money.getAmount();\r\n    }\r\n\r\n    public void minus(Money money){\r\n        this.amount =  this.amount - money.getAmount();\r\n    }\r\n\r\n    @Override\r\n    public boolean equals(Object o) {\r\n        if (this == o) return true;\r\n        if (o == null || getClass() != o.getClass()) return false;\r\n        Money money = (Money) o;\r\n        return amount == money.amount;\r\n    }\r\n\r\n    @Override\r\n    public int hashCode() {\r\n        return Objects.hash(amount);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/MoneyConverter.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge;\n\n\nimport jakarta.persistence.AttributeConverter;\nimport jakarta.persistence.Converter;\n\n@Converter(autoApply = true)\npublic class MoneyConverter implements AttributeConverter<Money,Long> {\n    @Override\n    public Long convertToDatabaseColumn(Money entityData) {\n        return Long.valueOf(entityData.getAmount());\n    }\n\n    @Override\n    public Money convertToEntityAttribute(Long dbData) {\n        return Money.of(dbData.intValue());\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/Session.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\n@Data\r\npublic class Session {\r\n    private String sessionId;\r\n\r\n    /**\r\n     * 主叫电话号码\r\n     */\r\n    private long callingPhoneNo;\r\n\r\n    /**\r\n     * 被叫电话号码\r\n     */\r\n    private long calledPhoneNo;\r\n\r\n    /**\r\n     * 当前通话已扣费的时长\r\n     *\r\n     */\r\n    private int chargedDuration;\r\n\r\n    /**\r\n     * 当前通话产生的Charge记录\r\n     */\r\n    private List<ChargeRecord> chargeRecordList = new ArrayList<>();\r\n\r\n    public Session(String sessionId, long callingPhoneNo, long calledPhoneNo) {\r\n        this.sessionId = sessionId;\r\n        this.callingPhoneNo = callingPhoneNo;\r\n        this.calledPhoneNo = calledPhoneNo;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargeplan/BasicChargePlan.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargeplan;\r\n\r\npublic class BasicChargePlan extends ChargePlan<BasicChargePlan.BasicChargeFee>{\r\n\r\n    public BasicChargePlan(){\r\n        this.priority = 0;\r\n    }\r\n\r\n    @Override\r\n    public BasicChargeFee getResource() {\r\n        return new BasicChargeFee();\r\n    }\r\n\r\n    @Override\r\n    public ChargePlanType getType() {\r\n        return ChargePlanType.BASIC;\r\n    }\r\n\r\n    public static class BasicChargeFee implements Resource{\r\n        /**\r\n         * 主叫单价。单位是角，5表示0.5元每分钟\r\n         */\r\n        public final int CALLING_PRICE = 5;\r\n\r\n        /**\r\n         * 主叫单价。单位是角，4表示0.4元每分钟\r\n         */\r\n        public final int CALLED_PRICE = 4;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargeplan/ChargePlan.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargeplan;\n\npublic abstract class ChargePlan<T extends Resource> implements Comparable<ChargePlan>{\n\n    protected int priority;\n\n    public abstract  T getResource();\n\n    public abstract ChargePlanType getType();\n\n    public ChargePlan(){\n\n    }\n    /**\n     * 不同套餐之间的优先级关系\n     * @param other the object to be compared.\n     * @return\n     */\n    @Override\n    public int compareTo(ChargePlan other) {\n        return other.priority - this.priority;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChargePlan{chargeType=\" + getType()+\n                \", priority=\" + priority +\n                '}';\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargeplan/ChargePlanType.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargeplan;\r\n\r\npublic enum ChargePlanType {\r\n    /**\r\n     * 基础套餐\r\n     */\r\n    BASIC,\r\n    /**\r\n     * 固定时常套餐\r\n     */\r\n    FIXED_TIME,\r\n    /**\r\n     * 家庭套餐\r\n     */\r\n    FAMILY\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargeplan/FamilyChargePlan.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargeplan;\r\n\r\nimport java.util.HashSet;\r\nimport java.util.Set;\r\n\r\npublic class FamilyChargePlan extends ChargePlan<FamilyChargePlan.FamilyMember> {\r\n\r\n    public FamilyChargePlan() {\r\n        this.priority = 2;\r\n    }\r\n\r\n    @Override\r\n    public FamilyMember getResource() {\r\n        return new FamilyMember();\r\n    }\r\n\r\n    @Override\r\n    public ChargePlanType getType() {\r\n        return ChargePlanType.FAMILY;\r\n    }\r\n\r\n    public static class FamilyMember implements Resource{\r\n        private Set<Long> familyMembers = new HashSet<>();\r\n\r\n        /**\r\n         * Mock here, 真实场景，情亲号码肯定也是从外系统获取的\r\n         */\r\n        public FamilyMember() {\r\n            familyMembers.add(13681874561L);\r\n            familyMembers.add(15921582125L);\r\n        }\r\n\r\n        public boolean isMember(long phoneNo) {\r\n            return familyMembers.contains(phoneNo);\r\n        }\r\n    }\r\n}\r\n\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargeplan/FixedTimeChangePlan.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargeplan;\r\n\r\npublic class FixedTimeChangePlan extends ChargePlan<FixedTimeChangePlan.FreeCallTime>{\r\n\r\n    public FixedTimeChangePlan() {\r\n        this.priority=1;\r\n    }\r\n\r\n    @Override\r\n    public FreeCallTime getResource() {\r\n        return new FreeCallTime();\r\n    }\r\n\r\n    @Override\r\n    public ChargePlanType getType() {\r\n        return ChargePlanType.FIXED_TIME;\r\n    }\r\n\r\n    public static class FreeCallTime implements Resource{\r\n        public static int FREE_CALLING_TIME = 200;\r\n        public static int FREE_CALLED_TIME = 200;\r\n\r\n        public boolean isCallingTimeRemaining(){\r\n            return FREE_CALLING_TIME > 0;\r\n        }\r\n\r\n        /**\r\n         * 扣减固定时长套餐的费用\r\n         * @param duration 扣减时长\r\n         * @return 剩余还需要扣减的时长\r\n         */\r\n        public int chargeFreeCallingTime(int duration){\r\n            if(duration > FREE_CALLING_TIME){\r\n                int durationToCharge = duration - FREE_CALLING_TIME;\r\n                FREE_CALLING_TIME = 0;\r\n                return durationToCharge;\r\n            }\r\n            else{\r\n                FREE_CALLING_TIME = FREE_CALLING_TIME - duration;\r\n                return 0;\r\n            }\r\n        }\r\n\r\n        public boolean isCalledTimeRemaining(){\r\n            return FREE_CALLED_TIME > 0;\r\n        }\r\n\r\n        /**\r\n         * 扣减固定时长套餐的费用\r\n         * @param duration 扣减时长\r\n         * @return 剩余还需要扣减的时长\r\n         */\r\n        public int chargeFreeCalledTime(int duration){\r\n            if(duration > FREE_CALLED_TIME){\r\n                int durationToCharge = duration - FREE_CALLED_TIME;\r\n                FREE_CALLED_TIME = 0;\r\n                return durationToCharge;\r\n            }\r\n            else{\r\n                FREE_CALLED_TIME = FREE_CALLED_TIME - duration;\r\n                return 0;\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargeplan/Resource.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargeplan;\r\n\r\n/**\r\n * 套餐背后所绑定的资源\r\n */\r\npublic interface Resource {\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargerule/AbstractChargeRule.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargerule;\r\n\r\nimport ${package}.domain.charge.chargeplan.ChargePlan;\r\n\r\npublic abstract class AbstractChargeRule implements ChargeRule{\r\n    protected ChargePlan chargePlan;\r\n\r\n    @Override\r\n    public void belongsTo(ChargePlan chargePlan){\r\n        this.chargePlan = chargePlan;\r\n    }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargerule/BasicChargeRule.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargerule;\r\n\r\nimport ${package}.domain.charge.*;\r\nimport ${package}.domain.charge.chargeplan.BasicChargePlan;\r\nimport ${package}.domain.charge.chargeplan.ChargePlanType;\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport org.springframework.stereotype.Component;\r\n\r\n@Component\r\n@Slf4j\r\npublic class BasicChargeRule extends AbstractChargeRule{\r\n    @Override\r\n    public ChargeRecord doCharge(ChargeContext ctx) {\r\n        if(!ctx.needCharge()){\r\n            log.debug(\"No need charge for : \"+ctx);\r\n            return null;\r\n        }\r\n        BasicChargePlan basicChargePlan = (BasicChargePlan)chargePlan;\r\n        BasicChargePlan.BasicChargeFee chargeFee = basicChargePlan.getResource();\r\n        Money cost;\r\n        int duration = ctx.durationToCharge;\r\n        if (ctx.callType == CallType.CALLING) {\r\n            cost = Money.of(duration * chargeFee.CALLING_PRICE);\r\n        } else {\r\n            cost = Money.of(duration * chargeFee.CALLED_PRICE);\r\n        }\r\n        ChargeRecord chargeRecord = new ChargeRecord(ctx.phoneNo, ctx.callType, duration, ChargePlanType.BASIC, cost);\r\n\r\n        //在账号上扣减费用\r\n        ctx.account.getRemaining().minus(cost);\r\n        ctx.setDurationToCharge(0);\r\n        return chargeRecord;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargerule/ChargeRule.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargerule;\r\n\r\nimport ${package}.domain.charge.ChargeRecord;\r\nimport ${package}.domain.charge.ChargeContext;\r\nimport ${package}.domain.charge.chargeplan.ChargePlan;\r\n\r\npublic interface ChargeRule {\r\n    ChargeRecord doCharge(ChargeContext ctx);\r\n\r\n    void belongsTo(ChargePlan chargePlan);\r\n\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargerule/ChargeRuleFactory.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargerule;\r\n\r\nimport ${package}.domain.ApplicationContextHelper;\r\nimport ${package}.domain.charge.chargeplan.ChargePlan;\r\nimport ${package}.domain.charge.chargeplan.ChargePlanType;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.Collections;\r\nimport java.util.List;\r\n\r\npublic class ChargeRuleFactory {\r\n    public static CompositeChargeRule get(List<ChargePlan> chargePlanList) {\r\n        //按套餐的优先级进行排序\r\n        Collections.sort(chargePlanList);\r\n\r\n        List<ChargeRule> chargeRules = new ArrayList<>();\r\n        for (ChargePlan chargePlan : chargePlanList) {\r\n            ChargeRule chargeRule;\r\n            if (chargePlan.getType() == ChargePlanType.FAMILY) {\r\n                chargeRule = ApplicationContextHelper.getBean(FamilyChargeRule.class);\r\n            } else if (chargePlan.getType() == ChargePlanType.FIXED_TIME) {\r\n                chargeRule = ApplicationContextHelper.getBean(FixedTimeChargeRule.class);\r\n            } else {\r\n                chargeRule = ApplicationContextHelper.getBean(BasicChargeRule.class);\r\n            }\r\n            chargeRule.belongsTo(chargePlan);\r\n            chargeRules.add(chargeRule);\r\n        }\r\n        CompositeChargeRule compositeChargeRule = new CompositeChargeRule();\r\n        compositeChargeRule.chargeRules = chargeRules;\r\n        return compositeChargeRule;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargerule/CompositeChargeRule.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargerule;\n\nimport ${package}.domain.charge.ChargeRecord;\nimport ${package}.domain.charge.ChargeContext;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * 为了应对套餐组合\n * 组合模式（Composite pattern)\n */\npublic class CompositeChargeRule {\n    public List<ChargeRule> chargeRules;\n\n    public List<ChargeRecord> doCharge(ChargeContext chargeContext){\n        List<ChargeRecord> chargeRecords = new ArrayList<>();\n        for(ChargeRule chargeRule : chargeRules){\n            ChargeRecord chargeRecord = chargeRule.doCharge(chargeContext);\n            if(chargeRecord != null){\n                chargeRecord.setSessionId(chargeContext.getSession().getSessionId());\n                //fill fields for persistence needs\n                chargeRecord.setCreateTime(new Date());\n                chargeRecord.setUpdateTime(new Date());\n                chargeRecords.add(chargeRecord);\n            }\n        }\n        return chargeRecords;\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargerule/FamilyChargeRule.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargerule;\r\n\r\nimport ${package}.domain.charge.ChargeRecord;\r\nimport ${package}.domain.charge.ChargeContext;\r\nimport ${package}.domain.charge.Money;\r\nimport ${package}.domain.charge.chargeplan.ChargePlanType;\r\nimport ${package}.domain.charge.chargeplan.FamilyChargePlan;\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport org.springframework.stereotype.Component;\r\n\r\n@Component\r\n@Slf4j\r\npublic class FamilyChargeRule extends AbstractChargeRule {\r\n\r\n    @Override\r\n    public ChargeRecord doCharge(ChargeContext ctx) {\r\n        FamilyChargePlan familyChargePlan = (FamilyChargePlan) chargePlan;\r\n        FamilyChargePlan.FamilyMember familyMember = familyChargePlan.getResource();\r\n        if (familyMember.isMember(ctx.otherSidePhoneNo)) {\r\n            log.debug(\"Family Charge plan for Account : \" + ctx.account);\r\n            ChargeRecord chargeRecord = new ChargeRecord(ctx.phoneNo, ctx.callType, ctx.durationToCharge, ChargePlanType.FAMILY, Money.of(0));\r\n            ctx.setDurationToCharge(0);\r\n            return chargeRecord;\r\n        }\r\n        return null;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/charge/chargerule/FixedTimeChargeRule.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.charge.chargerule;\r\n\r\nimport ${package}.domain.charge.ChargeRecord;\r\nimport ${package}.domain.charge.ChargeContext;\r\nimport ${package}.domain.charge.Money;\r\nimport ${package}.domain.charge.chargeplan.ChargePlanType;\r\nimport ${package}.domain.charge.chargeplan.FixedTimeChangePlan;\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport org.springframework.stereotype.Component;\r\n\r\n@Component\r\n@Slf4j\r\npublic class FixedTimeChargeRule extends AbstractChargeRule {\r\n    @Override\r\n    public ChargeRecord doCharge(ChargeContext ctx) {\r\n        if(!ctx.needCharge()){\r\n            log.debug(\"No need charge for : \"+ctx);\r\n            return null;\r\n        }\r\n        FixedTimeChangePlan fixedTimeChangePlan = (FixedTimeChangePlan) chargePlan;\r\n        FixedTimeChangePlan.FreeCallTime freeCallTime = fixedTimeChangePlan.getResource();\r\n        if (ctx.isCalling() && freeCallTime.isCallingTimeRemaining()) {\r\n            int leftDuration = freeCallTime.chargeFreeCallingTime(ctx.durationToCharge);\r\n            log.debug(\"Calling Left Duration after FixedTimeCharge : \" + leftDuration);\r\n            ChargeRecord chargeRecord = new ChargeRecord(ctx.phoneNo, ctx.callType, ctx.durationToCharge - leftDuration, ChargePlanType.FIXED_TIME, Money.of(0));\r\n            ctx.setDurationToCharge(leftDuration);\r\n            return chargeRecord;\r\n        }\r\n        if (ctx.isCalled() && freeCallTime.isCalledTimeRemaining()) {\r\n            int leftDuration = freeCallTime.chargeFreeCalledTime(ctx.durationToCharge);\r\n            log.debug(\"Called Left Duration after FixedTimeCharge : \" + leftDuration);\r\n            ChargeRecord chargeRecord = new ChargeRecord(ctx.phoneNo, ctx.callType, ctx.durationToCharge - leftDuration, ChargePlanType.FIXED_TIME, Money.of(0));\r\n            ctx.setDurationToCharge(leftDuration);\r\n            return chargeRecord;\r\n        }\r\n        return null;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/gateway/AccountGateway.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.gateway;\r\n\r\nimport ${package}.domain.account.Account;\r\nimport ${package}.domain.charge.ChargeRecord;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 跟账户系统交互的网关（Gateway）\r\n *\r\n * @version 1.0\r\n */\r\npublic interface AccountGateway {\r\n\r\n    /**\r\n     * 根据用户号码获取账户信息（含计费项余额等信息）\r\n     *\r\n     * @param phoneNo 电话号码\r\n     * @return 账户信息\r\n     */\r\n    Account getAccount(long phoneNo);\r\n\r\n    /**\r\n     * 将扣费记录同步到账户中\r\n     *\r\n     * @param phoneNo 电话号码\r\n     * @param records 扣费记录\r\n     */\r\n    void sync(long phoneNo, List<ChargeRecord> records);\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/gateway/ChargeGateway.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.gateway;\n\nimport ${package}.domain.charge.ChargeRecord;\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.List;\n\n@Repository\npublic interface ChargeGateway extends JpaRepository<ChargeRecord, Long> {\n    public List<ChargeRecord> findBySessionId(String sessionId);\n\n    public ChargeRecord getBySessionId(String sessionId);\n\n    public List<ChargeRecord> findByPhoneNo(long phoneNo);\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/domain/gateway/SessionGateway.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.gateway;\r\n\r\nimport ${package}.domain.charge.Session;\r\n\r\npublic interface SessionGateway {\r\n\r\n    void create(Session session);\r\n\r\n    Session get(String sessionId);\r\n\r\n    void end(String sessionId);\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/infrastructure/AccountGatewayImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.infrastructure;\n\nimport ${package}.domain.account.Account;\nimport ${package}.domain.charge.ChargeRecord;\nimport ${package}.domain.charge.Money;\nimport ${package}.domain.gateway.AccountGateway;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.client.RestClient;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n@Component\n@Slf4j\npublic class AccountGatewayImpl implements AccountGateway {\n    private static final String GET_ACCOUNT_PATH = \"/v1/api/account/{account}\";\n    private static final String SYNC_ACCOUNT_PATH = \"/v1/api/account/account/{account}/sync\";\n\n    private Map<Long, Account> accountMap = new HashMap<>();\n\n    @Autowired\n    private RestClient restClient;\n\n    @Override\n    public Account getAccount(long phoneNo) {\n        Account account = restClient.get()\n                .uri(GET_ACCOUNT_PATH, phoneNo)\n                .accept(MediaType.APPLICATION_JSON)\n                .retrieve()\n                .body(Account.class);\n\n        return account;\n    }\n\n    @Override\n    public void sync(long phoneNo, List<ChargeRecord> records) {\n        // 更新账户系统\n        log.info(\"sync account info, to be implemented\");\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/infrastructure/RestClientBean.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.infrastructure;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.client.RestClient;\n\n@Configuration\npublic class RestClientBean {\n\n    @Value(\"${symbol_dollar}{REMOTE_BASE_URI:http://localhost:8080}\")\n    String baseURI;\n\n    @Bean\n    RestClient restClient() {\n        return RestClient.create(baseURI);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/java/infrastructure/SessionGatewayImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.infrastructure;\r\n\r\nimport ${package}.domain.BizException;\r\nimport ${package}.domain.charge.Session;\r\nimport ${package}.domain.gateway.SessionGateway;\r\nimport org.springframework.stereotype.Component;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\n@Component\r\npublic class SessionGatewayImpl implements SessionGateway {\r\n    private Map<String, Session> sessionMap = new HashMap<>();\r\n\r\n    @Override\r\n    public void create(Session session) {\r\n        sessionMap.put(session.getSessionId(), session);\r\n    }\r\n\r\n    @Override\r\n    public Session get(String sessionId) {\r\n        return sessionMap.get(sessionId);\r\n    }\r\n\r\n    @Override\r\n    public void end(String sessionId) {\r\n        //真实场景是逻辑删除，比如把session的状态标记为“已结束”。\r\n        sessionMap.remove(sessionId);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/resources/application.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://${MYSQL_SERVER:localhost}:${MYSQL_PORT:3306}/${MYSQL_DB_NAME:chargeDB}?serverTimezone=UTC\n    #如果运行出错，可以把连接写成下面的路径进行测试\n    #url: jdbc:mysql://localhost:3306/blogDB?useUnicode=true&characterEncoding=utf-8\n    username: ${MYSQL_USER_TEST:root}\n    password: ${MYSQL_PASSWORD_TEST:root}\n  jpa:\n    hibernate:\n      ddl-auto: update\n    show-sql: true\n\nserver:\n  port: 8081\n\nmy-name: default\nmy-age: default\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/main/resources/logback.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<configuration>\r\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\r\n\r\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\r\n        <encoder>\r\n            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>\r\n            <charset>utf8</charset>\r\n        </encoder>\r\n    </appender>\r\n\r\n    <!--rootLogger是默认的logger-->\r\n    <root level=\"INFO\">\r\n        <appender-ref ref=\"CONSOLE\"/>\r\n    </root>\r\n\r\n    <!--应用日志-->\r\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\r\n    <logger name=\"${groupId}\" level=\"DEBUG\"/>\r\n\r\n    <!--全局的访问日志-->\r\n    <logger name=\"com.alibaba.cola.catchlog\" level=\"DEBUG\"/>\r\n\r\n</configuration>\r\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/charge.http",
    "content": "### list charge records by sessionId\nGET http://localhost:8080/123145/chargeRecords\nAccept: application/json\n\n### end session\nPOST http://localhost:8080/session/123145/end?duration=10\nContent-Type: application/x-www-form-urlencoded\n\nduration=10\n\n### do charge\nPOST http://localhost:8080/session/123145/charge?duration=10\n\n\n### begin Session\nPOST http://localhost:8080/session/123145/begin?callingPhoneNo=13681874561&calledPhoneNo=15921252125\n\n<> 2022-11-03T150743.200.json\n\n\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/CleanArchTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package};\n\nimport org.junit.jupiter.api.Test;\n\npublic class CleanArchTest {\n    @Test\n    public void protect_clean_arch() {\n//        JavaClasses classes = new ClassFileImporter()\n//                .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)\n//                .importPackages(\"${package}\");\n//\n//        layeredArchitecture()\n//                .consideringOnlyDependenciesInLayers()\n//                .layer(\"adapter\").definedBy(\"${package}.adapter\")\n//                .layer(\"application\").definedBy(\"${package}.application\")\n//                .layer(\"domain\").definedBy(\"${package}.domain\")\n//                .layer(\"infrastructure\").definedBy(\"${package}.infrastructure\")\n//                .whereLayer(\"adapter\").mayNotBeAccessedByAnyLayer()\n//                //.whereLayer(\"domain\").mayOnlyBeAccessedByLayers(\"application\", \"infrastructure\")\n//                .as(\"The layer dependencies must be respected\")\n//                .because(\"we must follow the Clean Architecture principle\")\n//                .check(classes);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/TestsContainerBoot.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package};\n\nimport com.alibaba.cola.test.TestsContainer;\n\npublic class TestsContainerBoot {\n    public static void main(String[] args) {\n        TestsContainer.start();\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/application/ChargeServiceTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.application;\n\nimport com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;\nimport com.github.tomakehurst.wiremock.junit5.WireMockTest;\nimport ${package}.Application;\nimport ${package}.application.dto.BeginSessionRequest;\nimport ${package}.domain.BizException;\nimport ${package}.domain.gateway.AccountGateway;\nimport ${package}.domain.gateway.SessionGateway;\nimport ${package}.infrastructure.WireMockRegister;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.ContextConfiguration;\n\n\n\n@SpringBootTest\n@ContextConfiguration(classes = Application.class)\n@WireMockTest(httpPort = 8080)\npublic class ChargeServiceTest {\n\n    @Autowired\n    private ChargeServiceI chargeService;\n\n    @Autowired\n    private SessionGateway sessionGateway;\n\n    @Autowired\n    private AccountGateway accountGateway;\n\n\n    @Test\n    public void test_session_create(WireMockRuntimeInfo wmRuntimeInfo) {\n        WireMockRegister.registerStub(wmRuntimeInfo.getWireMock(), \"/fixture/wiremock/stub_account.json\");\n\n        BeginSessionRequest request = new BeginSessionRequest();\n        String sessionId = \"00002\";\n        request.setSessionId(sessionId);\n        request.setCallingPhoneNo(13681874563L);\n        request.setCalledPhoneNo(15921582125L);\n\n        chargeService.begin(request);\n\n        Assertions.assertEquals(sessionId, sessionGateway.get(sessionId).getSessionId());\n    }\n\n    @Test\n    public void test_remaining_insufficient(WireMockRuntimeInfo wmRuntimeInfo) {\n        WireMockRegister.registerStub(wmRuntimeInfo.getWireMock(), \"/fixture/wiremock/stub_insufficient_account.json\");\n\n        BeginSessionRequest request = new BeginSessionRequest();\n        String sessionId = \"00003\";\n        request.setSessionId(sessionId);\n        request.setCallingPhoneNo(13681874561L);\n        request.setCalledPhoneNo(15921582125L);\n\n        Exception exception = Assertions.assertThrows(BizException.class, () -> {\n            chargeService.begin(request);\n        });\n        String expectedMsg = \"has insufficient amount\";\n        String actualMsg = exception.getMessage();\n        Assertions.assertTrue(actualMsg.contains(expectedMsg));\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/domain/ChargeRecordPlanTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain;\n\n\nimport ${package}.domain.charge.chargeplan.BasicChargePlan;\nimport ${package}.domain.charge.chargeplan.ChargePlan;\nimport ${package}.domain.charge.chargeplan.ChargePlanType;\nimport ${package}.domain.charge.chargeplan.FamilyChargePlan;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class ChargeRecordPlanTest {\n\n    @Test\n    public void test_priority(){\n        ChargePlan basicChargePlan = new BasicChargePlan();\n        ChargePlan familyChargePlan = new FamilyChargePlan();\n        ChargePlan fixedTimeChargePlan = new FamilyChargePlan();\n        List<ChargePlan> chargePlanList =  new ArrayList<>();\n        chargePlanList.add(basicChargePlan);\n        chargePlanList.add(familyChargePlan);\n        chargePlanList.add(fixedTimeChargePlan);\n\n        Collections.sort(chargePlanList);\n\n        System.out.println(chargePlanList.get(0));\n        Assertions.assertEquals(ChargePlanType.FAMILY, chargePlanList.get(0).getType());\n\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/domain/ChargeRecordRuleTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain;\n\nimport ${package}.domain.account.Account;\nimport ${package}.domain.charge.CallType;\nimport ${package}.domain.charge.ChargeContext;\nimport ${package}.domain.charge.Money;\nimport ${package}.domain.charge.chargeplan.BasicChargePlan;\nimport ${package}.domain.charge.chargeplan.ChargePlan;\nimport ${package}.domain.charge.chargeplan.FamilyChargePlan;\nimport ${package}.domain.charge.chargeplan.FixedTimeChangePlan;\nimport ${package}.domain.charge.chargerule.BasicChargeRule;\nimport ${package}.domain.charge.chargerule.FamilyChargeRule;\nimport ${package}.domain.charge.chargerule.FixedTimeChargeRule;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collections;\n\npublic class ChargeRecordRuleTest {\n\n    @Test\n    public void test_basic_charge_rule(){\n        //prepare\n        ChargePlan chargePlan = new BasicChargePlan();\n        Account account = new Account(13681874561L, Money.of(200), Collections.singletonList(chargePlan));\n        ChargeContext ctx = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 20);\n        ctx.account = account;\n        System.out.println(\"Account before charge: \"+ account);\n\n        //do\n        BasicChargeRule basicChargeRule = new BasicChargeRule();\n        basicChargeRule.belongsTo(chargePlan);\n        basicChargeRule.doCharge(ctx);\n\n        //check\n        System.out.println(\"Account after charge: \"+ account);\n        Assertions.assertEquals( Money.of(100), ctx.account.getRemaining());\n        Assertions.assertEquals( 0, ctx.getDurationToCharge());\n    }\n\n    @Test\n    public void test_family_charge_rule(){\n        //prepare\n        FamilyChargePlan chargePlan = new FamilyChargePlan();\n        Account account = new Account(13681874561L, Money.of(200), Collections.singletonList(chargePlan));\n        ChargeContext ctx = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 20);\n        ctx.account = account;\n        System.out.println(\"Account before charge: \"+ account);\n\n        //do\n        FamilyChargeRule familyChargeRule = new FamilyChargeRule();\n        familyChargeRule.belongsTo(chargePlan);\n        familyChargeRule.doCharge(ctx);\n\n        //check\n        System.out.println(\"Account after charge: \"+ account);\n        Assertions.assertEquals( Money.of(200), ctx.account.getRemaining());\n        Assertions.assertEquals( 0, ctx.getDurationToCharge());\n    }\n\n    @Test\n    public void test_fixed_time_charge_rule(){\n        //prepare\n        FixedTimeChangePlan chargePlan = new FixedTimeChangePlan();\n        Account account = new Account(13681874561L, Money.of(200), Collections.singletonList(chargePlan));\n        ChargeContext ctx = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 180);\n        ctx.account = account;\n        System.out.println(\"Account before charge: \"+ account);\n\n        //do\n        FixedTimeChargeRule fixedTimeChargeRule = new FixedTimeChargeRule();\n        fixedTimeChargeRule.belongsTo(chargePlan);\n        fixedTimeChargeRule.doCharge(ctx);\n\n        //check\n        System.out.println(\"Account after charge: \"+ account);\n        Assertions.assertEquals( true, chargePlan.getResource().isCallingTimeRemaining());\n        Assertions.assertEquals( 0, ctx.getDurationToCharge());\n\n        // come a new charge\n        ChargeContext ctx2 = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 40);\n        ctx2.account = account;\n        fixedTimeChargeRule.doCharge(ctx2);\n        Assertions.assertEquals( false, chargePlan.getResource().isCallingTimeRemaining());\n        Assertions.assertEquals( 20, ctx2.getDurationToCharge());\n\n        //reset fixed time\n        FixedTimeChangePlan.FreeCallTime.FREE_CALLED_TIME = 200;\n        FixedTimeChangePlan.FreeCallTime.FREE_CALLING_TIME = 200;\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/domain/CompositeChargeRuleTestRecord.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain;\n\nimport ${package}.Application;\nimport ${package}.domain.account.Account;\nimport ${package}.domain.charge.*;\nimport ${package}.domain.charge.chargeplan.BasicChargePlan;\nimport ${package}.domain.charge.chargeplan.ChargePlan;\nimport ${package}.domain.charge.chargeplan.FamilyChargePlan;\nimport ${package}.domain.charge.chargeplan.FixedTimeChangePlan;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.ContextConfiguration;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\n@SpringBootTest\n@ContextConfiguration(classes = Application.class)\npublic class CompositeChargeRuleTestRecord {\n\n    private long callingPhoneNo = 13681874561L;\n    private long calledPhoneNo = 15921582125L;\n\n    @Test\n    public void test_basic_and_fixedTime_charge_rule(){\n        // prepare\n        List<ChargePlan> chargePlanList = new ArrayList<>();\n        chargePlanList.add(new BasicChargePlan());\n        chargePlanList.add(new FixedTimeChangePlan());\n        Account account = Account.valueOf(13681874561L, Money.of(200)); // for spring bean\n        account.setChargePlanList(chargePlanList);\n        ChargeContext ctx = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 220);\n        String sessionId = UUID.randomUUID().toString();\n        Session session = new Session(sessionId, callingPhoneNo, calledPhoneNo);\n        ctx.setSession(session);\n        ctx.account = account;\n\n        // do\n        List<ChargeRecord> chargeRecords = account.charge(ctx);\n        System.out.println(\"Account after charge: \"+ account);\n        // check\n        Assertions.assertEquals(2, chargeRecords.size());\n    }\n\n    @Test\n    public void test_basic_and_family_charge_rule(){\n        // prepare\n        List<ChargePlan> chargePlanList = new ArrayList<>();\n        chargePlanList.add(new BasicChargePlan());\n        chargePlanList.add(new FamilyChargePlan());\n        Account account = Account.valueOf(13681874561L, Money.of(200)); // for spring bean\n        account.setChargePlanList(chargePlanList);\n        ChargeContext ctx = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 220);\n        String sessionId = UUID.randomUUID().toString();\n        Session session = new Session(sessionId, callingPhoneNo, calledPhoneNo);\n        ctx.setSession(session);\n        ctx.account = account;\n\n        // do\n        List<ChargeRecord> chargeRecords = account.charge(ctx);\n        System.out.println(\"Account after charge: \"+ account);\n        // check\n        Assertions.assertEquals(1, chargeRecords.size());\n    }\n\n    @Test\n    public void test_all_charge_rule(){\n        // prepare\n        List<ChargePlan> chargePlanList = new ArrayList<>();\n        chargePlanList.add(new BasicChargePlan());\n        chargePlanList.add(new FamilyChargePlan());\n        chargePlanList.add(new FixedTimeChangePlan());\n        Account account = Account.valueOf(13681874561L, Money.of(200)); // for spring bean\n        account.setChargePlanList(chargePlanList);\n        ChargeContext ctx = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 220);\n        String sessionId = UUID.randomUUID().toString();\n        Session session = new Session(sessionId, callingPhoneNo, calledPhoneNo);\n        ctx.setSession(session);\n        ctx.account = account;\n\n        // do\n        List<ChargeRecord> chargeRecords = account.charge(ctx);\n        System.out.println(\"Account after charge: \"+ account);\n\n        // check\n        Assertions.assertEquals(1, chargeRecords.size());\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/infrastructure/AccountGatewayTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.infrastructure;\n\nimport com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;\nimport com.github.tomakehurst.wiremock.junit5.WireMockTest;\nimport ${package}.Application;\nimport ${package}.domain.account.Account;\nimport ${package}.domain.charge.Money;\nimport ${package}.domain.gateway.AccountGateway;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.ContextConfiguration;\n\n@SpringBootTest\n@ContextConfiguration(classes = Application.class)\n@WireMockTest(httpPort = 8080)\npublic class AccountGatewayTest {\n\n    @Autowired\n    AccountGateway accountGateway;\n\n    @Test\n    public void testGetAccount(WireMockRuntimeInfo wmRuntimeInfo) {\n        WireMockRegister.registerStub(wmRuntimeInfo.getWireMock(), \"/fixture/wiremock/stub_account.json\");\n\n        Account account = accountGateway.getAccount(15921582125L);\n        System.out.println(\"account : \" + account);\n\n        Assertions.assertEquals(account.getPhoneNo(), 15921582125L);\n        Assertions.assertEquals(account.getRemaining(), Money.of(400));\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/infrastructure/ChargeRecordRepoTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.infrastructure;\n\nimport ${package}.domain.charge.CallType;\nimport ${package}.domain.charge.ChargeRecord;\nimport ${package}.domain.charge.Money;\nimport ${package}.domain.charge.chargeplan.ChargePlanType;\nimport ${package}.domain.gateway.ChargeGateway;\n\nimport jakarta.annotation.Resource;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.UUID;\n\n@SpringBootTest\npublic class ChargeRecordRepoTest {\n    @Resource\n    private ChargeGateway chargeGateway;\n\n    private String sessionId;\n\n    @BeforeEach\n    public void setup(){\n        sessionId = UUID.randomUUID().toString();\n    }\n\n    @Test\n    public void testSave(){\n        ChargeRecord chargeRecord = new ChargeRecord(13681874561L, CallType.CALLED, 10, ChargePlanType.FAMILY, Money.of(123));\n        chargeRecord.setSessionId(sessionId);\n        chargeRecord.setCreateTime(new Date());\n        chargeRecord.setUpdateTime(new Date());\n        chargeGateway.save(chargeRecord);\n\n        chargeRecord = chargeGateway.getBySessionId(sessionId);\n\n        Assertions.assertEquals(chargeRecord.getSessionId(), sessionId);\n    }\n\n    @Test\n    public void testSaveList(){\n        List<ChargeRecord> chargeRecordList = new ArrayList<>();\n        ChargeRecord chargeRecord1 = new ChargeRecord(13681874561L, CallType.CALLED, 10, ChargePlanType.FAMILY, Money.of(123));\n        chargeRecord1.setSessionId(UUID.randomUUID().toString());\n        ChargeRecord chargeRecord2 = new ChargeRecord(13681874561L, CallType.CALLING, 10, ChargePlanType.FAMILY, Money.of(123));\n        chargeRecord2.setSessionId(UUID.randomUUID().toString());\n        chargeRecordList.add(chargeRecord1);\n        chargeRecordList.add(chargeRecord2);\n        chargeGateway.saveAll(chargeRecordList);\n\n        List<ChargeRecord> result = chargeGateway.findByPhoneNo(13681874561L);\n\n        Assertions.assertEquals(result.size(), 2);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/infrastructure/FixtureLoader.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.infrastructure;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UncheckedIOException;\nimport java.nio.charset.StandardCharsets;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.util.StreamUtils;\n\npublic class FixtureLoader {\n\n    public static String loadResource(String resourcePath) {\n        // 创建一个 ClassPathResource 对象\n        ClassPathResource resource = new ClassPathResource(resourcePath);\n\n        // 使用 withResource 来自动关闭输入流\n        String content = \"\";\n        try (InputStream inputStream = resource.getInputStream()) {\n            content = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);\n        } catch (IOException e) {\n            e.printStackTrace();\n            throw new RuntimeException(e.getMessage());\n        }\n        return content;\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/infrastructure/JSONTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.infrastructure;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport ${package}.domain.account.Account;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\npublic class JSONTest {\n\n    @Test\n    public void testJsonBind() {\n        // this will throw exception since account not recognized\n        String badJson = \"{${symbol_escape}\"account${symbol_escape}\":{${symbol_escape}\"name${symbol_escape}\":${symbol_escape}\"frank${symbol_escape}\",${symbol_escape}\"phoneNo${symbol_escape}\":${symbol_escape}\"15921582125${symbol_escape}\",${symbol_escape}\"remaining${symbol_escape}\":${symbol_escape}\"400${symbol_escape}\",${symbol_escape}\"chargePlanList${symbol_escape}\":[{${symbol_escape}\"priority${symbol_escape}\":${symbol_escape}\"2${symbol_escape}\",${symbol_escape}\"type${symbol_escape}\":${symbol_escape}\"fixedTime${symbol_escape}\"},{${symbol_escape}\"priority${symbol_escape}\":${symbol_escape}\"1${symbol_escape}\",${symbol_escape}\"type${symbol_escape}\":${symbol_escape}\"familyMember${symbol_escape}\"}]}}\";\n        // this is good\n        String goodJson = \"{${symbol_escape}\"name${symbol_escape}\":${symbol_escape}\"frank${symbol_escape}\",${symbol_escape}\"phoneNo${symbol_escape}\":${symbol_escape}\"15921582125${symbol_escape}\",${symbol_escape}\"remaining${symbol_escape}\":${symbol_escape}\"400${symbol_escape}\",${symbol_escape}\"chargePlanList${symbol_escape}\":[{${symbol_escape}\"priority${symbol_escape}\":${symbol_escape}\"2${symbol_escape}\",${symbol_escape}\"type${symbol_escape}\":${symbol_escape}\"fixedTime${symbol_escape}\"},{${symbol_escape}\"priority${symbol_escape}\":${symbol_escape}\"1${symbol_escape}\",${symbol_escape}\"type${symbol_escape}\":${symbol_escape}\"familyMember${symbol_escape}\"}]}\";\n\n        try {\n            ObjectMapper objectMapper = new ObjectMapper();\n            Account account = objectMapper.readValue(goodJson, Account.class);\n            Assertions.assertEquals(account.getPhoneNo(), 15921582125L);\n            System.out.println(account);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/infrastructure/SpingBootConfTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.infrastructure;\n\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.ActiveProfiles;\n\n\n@SpringBootTest\n//use test profile, this will merge application.yml and application-test.yaml.\n//but if you put an application.yml under test/resources, it will replace the project application.yml.\n//the advantage of profile, is that it can inherit and override.\n@ActiveProfiles(\"test\")\npublic class SpingBootConfTest {\n\n    @Value(\"${symbol_dollar}{spring.jpa.show-sql}\")\n    private String showSql;\n\n    @Value(\"${symbol_dollar}{spring.jpa.hibernate.ddl-auto}\")\n    private String ddlAuto;\n\n    @Value(\"${symbol_dollar}{my-name}\")\n    private String myName;\n\n    @Value(\"${symbol_dollar}{my-age}\")\n    private String myAge;\n\n    @Value(\"${symbol_dollar}{my-age-test}\")\n    private String myAgeTest;\n\n\n    @Test\n    public void test() {\n        System.out.println(\"spring.jpa.show-sql : \" + showSql);\n        System.out.println(\"spring.jpa.hibernate.ddl-auto : \" + ddlAuto);\n        System.out.println(\"myName : \" + myName);\n        System.out.println(\"myAge : \" + myAge);\n        System.out.println(\"myAgeTest : \" + myAgeTest);\n\n        Assertions.assertEquals(\"30\", myAge);\n        Assertions.assertEquals(\"40\", myAgeTest);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/infrastructure/WireMockBasicTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.infrastructure;\n\n\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;\nimport com.github.tomakehurst.wiremock.junit5.WireMockTest;\nimport ${package}.Application;\nimport ${package}.domain.account.Account;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.http.MediaType;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\n\n@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@WireMockTest(httpPort = 8080)\n@Slf4j\npublic class WireMockBasicTest {\n\n    @Autowired\n    protected WebTestClient webClient;\n\n    @Test\n    public void testWireMockBasic() {\n        // The static DSL will be automatically configured for you\n        stubFor(get(\"/static-dsl\").willReturn(ok()));\n\n        webClient.get()\n                .uri(\"http://localhost:8080/static-dsl\")\n                .exchange()\n                .expectStatus()\n                .isEqualTo(200);\n    }\n\n    @Test\n    public void testWireMockStub(WireMockRuntimeInfo wmRuntimeInfo) {\n        WireMock wireMock = wmRuntimeInfo.getWireMock();\n        WireMockRegister.registerStub(wireMock, \"/fixture/wiremock/stub_wire_mock_basic.json\");\n\n        webClient.get()\n                .uri(\"http://localhost:8080/v1/wiremock/basic\")\n                .exchange()\n                .expectStatus()\n                .isEqualTo(200)\n                .expectHeader()\n                .contentType(MediaType.APPLICATION_JSON);\n\n        System.out.println(\"wire mock serer port : \" + wmRuntimeInfo.getHttpPort());\n    }\n\n    @Test\n    public void testWireMockAccount(WireMockRuntimeInfo wmRuntimeInfo) {\n        WireMockRegister.registerStub(wmRuntimeInfo.getWireMock(), \"/fixture/wiremock/stub_account.json\");\n\n        long phoneNo = 123456789;\n\n        webClient.get()\n                .uri(\"http://localhost:8080/v1/api/account/\"+phoneNo)\n                .exchange()\n                .expectStatus()\n                .isEqualTo(200)\n                .expectHeader()\n                .contentType(MediaType.APPLICATION_JSON)\n                .returnResult(Account.class)\n                .getResponseBody()\n                .map(account -> {\n                    log.info(account.toString());\n                    Assertions.assertEquals(\"frank\", account.getName());\n                    Assertions.assertEquals(phoneNo, account.getPhoneNo());\n                    return account;\n                })\n                .subscribe();\n\n        log.info(\"wire mock serer port : \" + wmRuntimeInfo.getHttpPort());\n    }\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/java/infrastructure/WireMockRegister.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.infrastructure;\n\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wiremock.stubbing.StubMapping;\n\npublic class WireMockRegister {\n\n    public static void registerStub(WireMock wireMock, String resourcePath){\n        StubMapping stubMapping = StubMapping.buildFrom(FixtureLoader.loadResource(resourcePath));\n        wireMock.register(stubMapping);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/resources/application-test.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    # hard code for test purpose\n    url: jdbc:mysql://localhost:3306/chargeDB?serverTimezone=UTC\n    username: root\n    password: root\n\n  jpa:\n    hibernate:\n      ddl-auto: update\n    show-sql: true\n\n# this will override config in test/resources/application.yml and resources/application.yml\nmy-age: 30\n\nmy-age-test: 40\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/resources/application.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: org.h2.Driver\n    url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=1\n    username: sa\n    password:\n\n  jpa:\n    hibernate:\n      ddl-auto: update\n    show-sql: true\n\nserver:\n  port: 8081\n\nmy-name: frank\nmy-age: 35\nREMOTE_BASE_URI: http://localhost:8080\n\n\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/resources/fixture/wiremock/stub_account.json",
    "content": "{\n  \"request\": {\n    \"urlPathPattern\": \"/v1/api/account/[0-9]+\",\n    \"method\": \"GET\"\n  },\n  \"response\": {\n    \"status\": 200,\n    \"headers\": {\n      \"Content-Type\": \"application/json\"\n    },\n    \"transformers\": [\n      \"response-template\"\n    ],\n    \"jsonBody\": {\n      \"name\": \"frank\",\n      \"phoneNo\": \"{{request.path.[3]}}\",\n      \"remaining\": \"400\",\n      \"chargePlanList\": [\n        {\n          \"priority\": \"2\",\n          \"type\": \"fixedTime\"\n        },\n        {\n          \"priority\": \"1\",\n          \"type\": \"familyMember\"\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/resources/fixture/wiremock/stub_insufficient_account.json",
    "content": "{\n  \"request\": {\n    \"urlPathPattern\": \"/v1/api/account/[0-9]+\",\n    \"method\": \"GET\"\n  },\n  \"response\": {\n    \"status\": 200,\n    \"headers\": {\n      \"Content-Type\": \"application/json\"\n    },\n    \"transformers\": [\n      \"response-template\"\n    ],\n    \"jsonBody\": {\n      \"name\": \"frank\",\n      \"phoneNo\": \"{{request.path.[3]}}\",\n      \"remaining\": \"0\",\n      \"chargePlanList\": [\n        {\n          \"priority\": \"2\",\n          \"type\": \"fixedTime\"\n        },\n        {\n          \"priority\": \"1\",\n          \"type\": \"familyMember\"\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/resources/fixture/wiremock/stub_wire_mock_basic.json",
    "content": "{\n  \"request\": {\n    \"urlPathPattern\": \"/v1/wiremock/basic\",\n    \"method\": \"GET\"\n  },\n  \"response\": {\n    \"status\": 200,\n    \"headers\": {\n      \"Content-Type\": \"application/json\"\n    },\n    \"jsonBody\": {\n      \"request_id\": \"f7f9e747-f073-4ea8-8360-b42fc754a049\",\n      \"hyper_switch\": {\n        \"id\": \"switch1\",\n        \"name\": \"1520-001\",\n        \"device_model\": \"1520\",\n        \"role\": \"tor\",\n        \"mgmt_ip\": \"192.168.0.1\",\n        \"rack_code\": \"kw14b2-1k-01-08\",\n        \"sn\": \"21980119523GP8000745\",\n        \"node_id\": \"node1\",\n        \"xpod_id\": \"pod1\",\n        \"op_status\": \"online\",\n        \"created_at\": \"2024-02-04 15:11:13\",\n        \"updated_at\": \"2020-02-04 15:11:13\",\n        \"type\": \"l1\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/main/resources/archetype-resources/src/test/resources/logback-test.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%date{HH:mm:ss} %highlight(%-5level) [%blue(%t)] %yellow(%C{35}): %msg%n%throwable</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n\n    <!--应用日志-->\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n    <logger name=\"${groupId}\" level=\"DEBUG\"/>\n    <!--hibernate6以上的配置，SQL参数绑定日志-->\n    <logger name=\"org.hibernate.SQL\" level=\"debug\"/>\n    <logger name=\"org.hibernate.orm.jdbc.bind\" level=\"trace\"/>\n\n    <!--全局的访问日志-->\n    <logger name=\"com.alibaba.cola.catchlog\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/test/resources/projects/basic/archetype.properties",
    "content": "#Sun May 12 20:30:31 CST 2024\npackage=it.pkg\ngroupId=archetype.it\nartifactId=basic\nversion=0.1-SNAPSHOT\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-light/src/test/resources/projects/basic/goal.txt",
    "content": ""
  },
  {
    "path": "cola-archetypes/cola-archetype-service/.gitignore",
    "content": "target/\n\n### eclipse ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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    <parent>\n        <groupId>com.alibaba.cola</groupId>\n        <artifactId>cola-framework-archetypes-parent</artifactId>\n        <version>5.x-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>cola-framework-archetype-service</artifactId>\n    <packaging>maven-archetype</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/META-INF/maven/archetype-metadata.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archetype-descriptor\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd\"\n    name=\"demo\"\n    xmlns=\"http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0\">\n\n    <requiredProperties>\n        <requiredProperty key=\"gitignore\">\n            <defaultValue>.gitignore</defaultValue>\n        </requiredProperty>\n    </requiredProperties>\n\n    <fileSets>\n        <fileSet>\n            <directory></directory>\n            <includes>\n                <include>__gitignore__</include>\n            </includes>\n        </fileSet>\n    </fileSets>\n\n    <modules>\n        <module id=\"${rootArtifactId}-client\" dir=\"__rootArtifactId__-client\" name=\"${rootArtifactId}-client\">\n            <fileSets>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n            </fileSets>\n        </module>\n\n        <module id=\"${rootArtifactId}-app\" dir=\"__rootArtifactId__-app\" name=\"${rootArtifactId}-app\">\n            <fileSets>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/test/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n            </fileSets>\n        </module>\n\n        <module id=\"${rootArtifactId}-domain\" dir=\"__rootArtifactId__-domain\" name=\"${rootArtifactId}-domain\">\n            <fileSets>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/test/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n            </fileSets>\n        </module>\n\n        <module id=\"${rootArtifactId}-infrastructure\" dir=\"__rootArtifactId__-infrastructure\"\n                name=\"${rootArtifactId}-infrastructure\">\n            <fileSets>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/resources</directory>\n                    <includes>\n                        <include>**/*.xml</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/test/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" encoding=\"UTF-8\">\n                    <directory>src/test/resources</directory>\n                    <includes>\n                        <include>**/*.properties</include>\n                    </includes>\n                </fileSet>\n            </fileSets>\n        </module>\n\n        <module id=\"start\" dir=\"start\" name=\"start\">\n            <fileSets>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/resources</directory>\n                    <includes>\n                        <include>**/*.xml</include>\n                        <include>**/*.properties</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/test/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" encoding=\"UTF-8\">\n                    <directory>src/test/resources</directory>\n                    <includes>\n                        <include>**/*.xml</include>\n                        <include>**/*.properties</include>\n                    </includes>\n                </fileSet>\n            </fileSets>\n        </module>\n    </modules>\n</archetype-descriptor>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__gitignore__",
    "content": "target/\n\n### eclipse ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-app/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>${rootArtifactId}-parent</artifactId>\n        <version>${version}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${artifactId}</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>${groupId}</groupId>\n            <artifactId>${rootArtifactId}-client</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>${groupId}</groupId>\n            <artifactId>${rootArtifactId}-infrastructure</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-catchlog-starter</artifactId>\n        </dependency>\n\n        <!-- JSR 303 Validation -->\n        <dependency>\n            <groupId>org.hibernate.validator</groupId>\n            <artifactId>hibernate-validator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>javax.el</groupId>\n            <artifactId>javax.el-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.glassfish</groupId>\n            <artifactId>jakarta.el</artifactId>\n        </dependency>\n        <!-- JSR 303 Validation End-->\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-app/src/main/java/customer/CustomerServiceImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.customer;\n\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.catchlog.CatchAndLog;\nimport ${package}.api.CustomerServiceI;\nimport ${package}.dto.CustomerAddCmd;\nimport ${package}.dto.CustomerListByNameQry;\nimport ${package}.dto.data.CustomerDTO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport ${package}.customer.executor.CustomerAddCmdExe;\nimport ${package}.customer.executor.query.CustomerListByNameQryExe;\n\nimport javax.annotation.Resource;\n\n@Service\n@CatchAndLog\npublic class CustomerServiceImpl implements CustomerServiceI {\n\n    @Resource\n    private CustomerAddCmdExe customerAddCmdExe;\n\n    @Resource\n    private CustomerListByNameQryExe customerListByNameQryExe;\n\n    public Response addCustomer(CustomerAddCmd customerAddCmd) {\n        return customerAddCmdExe.execute(customerAddCmd);\n    }\n\n    @Override\n    public MultiResponse<CustomerDTO> listByName(CustomerListByNameQry customerListByNameQry) {\n        return customerListByNameQryExe.execute(customerListByNameQry);\n    }\n\n}"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-app/src/main/java/customer/executor/CustomerAddCmdExe.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n\npackage ${package}.customer.executor;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.exception.BizException;\nimport ${package}.dto.CustomerAddCmd;\nimport ${package}.dto.data.ErrorCode;\nimport org.springframework.stereotype.Component;\n\n\n@Component\npublic class CustomerAddCmdExe{\n\n    public Response execute(CustomerAddCmd cmd) {\n        //The flow of usecase is defined here.\n        //The core ablility should be implemented in Domain. or sink to Domian gradually\n        if(cmd.getCustomerDTO().getCompanyName().equals(\"ConflictCompanyName\")){\n            throw new BizException(ErrorCode.B_CUSTOMER_companyNameConflict, \"公司名冲突\");\n        }\n        return Response.buildSuccess();\n    }\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-app/src/main/java/customer/executor/query/CustomerListByNameQryExe.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.customer.executor.query;\n\nimport com.alibaba.cola.dto.MultiResponse;\nimport ${package}.dto.CustomerListByNameQry;\nimport ${package}.dto.data.CustomerDTO;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.springframework.stereotype.Component;\n\n\n@Component\npublic class CustomerListByNameQryExe{\n    public MultiResponse<CustomerDTO> execute(CustomerListByNameQry cmd) {\n        List<CustomerDTO> customerDTOList = new ArrayList<>();\n        CustomerDTO customerDTO = new CustomerDTO();\n        customerDTO.setCustomerName(\"Frank\");\n        customerDTOList.add(customerDTO);\n        return MultiResponse.of(customerDTOList);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-app/src/main/java/order/OrderServiceImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.order;\n\n//package by domain, not by duty\n\n\npublic class OrderServiceImpl{\n\n}"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-app/src/test/java/app/CustomerConvertorTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.app;\n\n\npublic class CustomerConvertorTest {\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-app/src/test/java/app/CustomerValidatorTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.app;\n\nimport org.junit.Test;\n\npublic class CustomerValidatorTest {\n\n    @Test\n    public void testValidation(){\n\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>${rootArtifactId}-parent</artifactId>\n        <version>${version}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${parentArtifactId}-client</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-dto</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>javax.validation</groupId>\n            <artifactId>validation-api</artifactId>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/api/CustomerServiceI.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.api;\n\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.cola.dto.Response;\nimport ${package}.dto.CustomerAddCmd;\nimport ${package}.dto.CustomerListByNameQry;\nimport ${package}.dto.data.CustomerDTO;\n\npublic interface CustomerServiceI {\n\n    Response addCustomer(CustomerAddCmd customerAddCmd);\n\n    MultiResponse<CustomerDTO> listByName(CustomerListByNameQry customerListByNameQry);\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/dto/CustomerAddCmd.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.dto;\n\nimport ${package}.dto.data.CustomerDTO;\nimport lombok.Data;\n\n@Data\npublic class CustomerAddCmd{\n\n    private CustomerDTO customerDTO;\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/dto/CustomerListByNameQry.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.dto;\n\nimport com.alibaba.cola.dto.Query;\nimport lombok.Data;\n\n@Data\npublic class CustomerListByNameQry extends Query{\n   private String name;\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/dto/data/CustomerDTO.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.dto.data;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n@Data\npublic class CustomerDTO{\n    private String customerId;\n    private String memberId;\n    private String customerName;\n    private String customerType;\n    @NotEmpty\n    private String companyName;\n    @NotEmpty\n    private String source;\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/dto/data/ErrorCode.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.dto.data;\n\n\npublic class ErrorCode {\n    public static final String B_CUSTOMER_companyNameConflict = \"B_CUSTOMER_companyNameConflict\";\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/dto/event/CustomerCreatedEvent.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.dto.event;\n\nimport static ${package}.dto.event.DomainEventConstant.CUSTOMER_CREATED_TOPIC;\n\n/**\n * CustomerCreatedEvent\n *\n * @author Frank Zhang\n * @date 2019-01-04 10:32 AM\n */\npublic class CustomerCreatedEvent{\n\n    private String customerId;\n\n    public String getCustomerId() {\n        return customerId;\n    }\n\n    public void setCustomerId(String customerId) {\n        this.customerId = customerId;\n    }\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/dto/event/DomainEventConstant.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.dto.event;\n\n/**\n * @author niexiaolong\n * @date 2019/4/16\n */\npublic class DomainEventConstant {\n\n\tpublic static final String CUSTOMER_CREATED_TOPIC = \"CRM_CUSTOMER_CREATED_DOMAIN_EVENT_TOPIC\";\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-domain/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>${rootArtifactId}-parent</artifactId>\n        <version>${version}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${artifactId}</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>${groupId}</groupId>\n            <artifactId>${rootArtifactId}-client</artifactId>\n        </dependency>\n\n        <!-- COLA components -->\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-domain-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-exception</artifactId>\n        </dependency>\n        <!-- COLA components End-->\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/CompanyType.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer;\n\n/**\n * CompanyType\n *\n * @author Frank Zhang\n * @date 2018-01-08 11:02 AM\n */\npublic enum CompanyType {\n    POTENTIAL,\n    INTENTIONAL,\n    IMPORTANT,\n    VIP;\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/Credit.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer;\n\nimport com.alibaba.cola.domain.Entity;\nimport lombok.Data;\n\n@Data\n@Entity\npublic class Credit{\n    \n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/Customer.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer;\n\nimport com.alibaba.cola.domain.Entity;\nimport com.alibaba.cola.exception.BizException;\nimport lombok.Data;\n\n//Domain Entity can choose to extends the domain model which is used for DTO\n@Data\n@Entity\npublic class Customer{\n\n    private String customerId;\n    private String memberId;\n    private String globalId;\n    private long registeredCapital;\n    private String companyName;\n    private SourceType sourceType;\n    private CompanyType companyType;\n\n    public Customer() {\n    }\n\n    public boolean isBigCompany() {\n        return registeredCapital > 10000000; //注册资金大于1000万的是大企业\n    }\n\n    public boolean isSME() {\n        return registeredCapital > 10000 && registeredCapital < 1000000; //注册资金大于10万小于100万的为中小企业\n    }\n\n    public void checkConflict(){\n        //Per different biz, the check policy could be different, if so, use ExtensionPoint\n        if(\"ConflictCompanyName\".equals(this.companyName)){\n            throw new BizException(this.companyName+\" has already existed, you can not add it\");\n        }\n\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/CustomerType.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer;\n\n/**\n * CustomerType\n *\n * @author Frank Zhang\n * @date 2018-01-08 8:51 AM\n */\npublic enum CustomerType {\n    POTENTIAL,\n    INTENTIONAL,\n    IMPORTANT,\n    VIP;\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/SourceType.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer;\n\n/**\n * SourceType\n *\n * @author Frank Zhang\n * @date 2018-01-08 11:09 AM\n */\npublic enum SourceType {\n    BIZ_ONE, //From biz one\n    BIZ_TWO; //From biz two\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/domainservice/CreditChecker.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer.domainservice;\n\n//The domain's ablility can also be placed here\npublic class CreditChecker{\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/gateway/CreditGateway.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer.gateway;\n\nimport ${package}.domain.customer.Customer;\nimport ${package}.domain.customer.Credit;\n\n//Assume that the credit info is in antoher distributed Service\npublic interface CreditGateway {\n    Credit getCredit(String customerId);\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/gateway/CustomerGateway.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer.gateway;\n\nimport ${package}.domain.customer.Customer;\n\npublic interface CustomerGateway {\n    Customer getByById(String customerId);\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/order/Order.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.order;\n\npublic class Order{\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/package-info.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n/**\n * This is domain module, the core business logic is implemented here.\n * \n * @author fulan.zjf\n */\npackage ${package}.domain;"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/test/java/domain/CustomerEntityTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain;\n\n\npublic class CustomerEntityTest {\n\n    public void testCustomerConflict() {\n        System.out.println(\"Please mock gatewayimpl, test pure Domain Knowledge\");\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>${rootArtifactId}-parent</artifactId>\n        <version>${version}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${artifactId}</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>${groupId}</groupId>\n            <artifactId>${rootArtifactId}-domain</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/java/config/DiamondConfig.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.config;\n\npublic class DiamondConfig {\n    public final static String DummyConfig = \"DummyConfig\";\n}"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/java/customer/CreditGatewayImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.customer;\n\nimport ${package}.domain.customer.Credit;\nimport ${package}.domain.customer.gateway.CreditGateway;\n\npublic class CreditGatewayImpl implements CreditGateway {\n    public Credit getCredit(String customerId){\n      return null;\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/java/customer/CustomerDO.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.customer;\n\nimport lombok.Data;\n\n@Data\npublic class CustomerDO{\n  private String customerId;\n  private String memberId;\n  private String globalId;\n  private long registeredCapital;\n  private String companyName;\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/java/customer/CustomerGatewayImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.customer;\n\nimport ${package}.domain.customer.Customer;\nimport ${package}.domain.customer.gateway.CustomerGateway;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CustomerGatewayImpl implements CustomerGateway {\n    @Autowired\n    private CustomerMapper customerMapper;\n\n    public Customer getByById(String customerId){\n      CustomerDO customerDO = customerMapper.getById(customerId);\n      //Convert to Customer\n      return null;\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/java/customer/CustomerMapper.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.customer;\n\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface CustomerMapper{\n\n  CustomerDO getById(String customerId);\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/java/order/OrderGatewayImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.order;\n\npublic class OrderGatewayImpl{\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/resources/logback-spring.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <!-- https://github.com/spring-projects/spring-boot/blob/v1.4.2.RELEASE/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n    <property name=\"APP_NAME\" value=\"${parentArtifactId}\" />\n    <property name=\"LOG_PATH\" value=\"${symbol_dollar}{user.home}/${symbol_dollar}{APP_NAME}/logs\" />\n    <property name=\"LOG_FILE\" value=\"${symbol_dollar}{LOG_PATH}/application.log\" />\n\n    <appender name=\"APPLICATION\"\n        class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${symbol_dollar}{LOG_FILE}</file>\n        <encoder>\n            <pattern>${symbol_dollar}{FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${symbol_dollar}{LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>\n            <maxHistory>7</maxHistory>\n            <maxFileSize>50MB</maxFileSize>\n            <totalSizeCap>20GB</totalSizeCap>\n        </rollingPolicy>\n    </appender>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${symbol_dollar}{CONSOLE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\" />\n        <appender-ref ref=\"APPLICATION\" />\n    </root>\n</configuration>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/resources/mybatis/customer-mapper.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"${package}.datatunnel.CustomerTunnelI\">\n    <select id=\"findByCriteria\" resultType=\"${package}.database.dataobject.CustomerDO\" resultMap=\"customerMap\">\n        select * from customer where customer_id = ${symbol_pound}{id}\n    </select>\n    <resultMap type=\"${package}.database.dataobject.CustomerDO\" id=\"customerMap\">\n         <result property=\"customerId\" column=\"emp_no\"/>\n         <result property=\"memberId\" column=\"salary\"/>\n         <result property=\"globalId\" column=\"from_date\"/>\n     </resultMap>\n</mapper>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/resources/mybatis/mybatis-config.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!-- mybatis的配置文件 -->\n<!DOCTYPE configuration\n        PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\t<mappers>\n        <mapper resource=\"mybatis/customer-mapper.xml\"/>\n    </mappers>\n</configuration>"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/test/java/repository/CustomerMapperTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.repository;\n\n\npublic class CustomerMapperTest {\n\n    public void testFindByID() {\n        System.out.println(\"Write your test here\");\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/test/resources/sample.properties",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>${groupId}</groupId>\n    <artifactId>${artifactId}-parent</artifactId>\n    <version>${version}</version>\n    <packaging>pom</packaging>\n    <name>${artifactId}</name>\n\n    <properties>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <maven.compiler.target>${maven.compiler.source}</maven.compiler.target>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <maven.deploy.skip>true</maven.deploy.skip>\n\n        <cola.components.version>5.x-SNAPSHOT</cola.components.version>\n\n        <spring-boot.version>2.7.2</spring-boot.version>\n        <mybatis-starter.version>2.2.2</mybatis-starter.version>\n    </properties>\n\n    <modules>\n        <module>${rootArtifactId}-client</module>\n        <module>${rootArtifactId}-app</module>\n        <module>${rootArtifactId}-domain</module>\n        <module>${rootArtifactId}-infrastructure</module>\n        <module>start</module>\n    </modules>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <!--Project modules-->\n            <dependency>\n                <groupId>${groupId}</groupId>\n                <artifactId>${rootArtifactId}-client</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>${groupId}</groupId>\n                <artifactId>${rootArtifactId}-app</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>${groupId}</groupId>\n                <artifactId>${rootArtifactId}-domain</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>${groupId}</groupId>\n                <artifactId>${rootArtifactId}-infrastructure</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <!--Project modules End-->\n\n            <dependency>\n                <groupId>com.alibaba.cola</groupId>\n                <artifactId>cola-components-bom</artifactId>\n                <version>${cola.components.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-dependencies</artifactId>\n                <version>${spring-boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.mybatis.spring.boot</groupId>\n                <artifactId>mybatis-spring-boot-starter</artifactId>\n                <version>${mybatis-starter.version}</version>\n            </dependency>\n\n            <!--Validation API-->\n            <!--\n                - javax.validation:javax.validation\n                - org.hibernate.validator:hibernate-validator\n                - org.glassfish:jakarta.el\n                are declared in spring-boot-dependencies\n            -->\n            <dependency>\n                <groupId>javax.el</groupId>\n                <artifactId>javax.el-api</artifactId>\n                <version>3.0.0</version>\n            </dependency>\n            <!--Validation API End -->\n\n            <!-- Misc -->\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>fastjson</artifactId>\n                <version>1.2.83</version>\n            </dependency>\n            <!-- Misc End -->\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <artifactId>maven-resources-plugin</artifactId>\n                    <version>3.3.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-compiler-plugin</artifactId>\n                    <version>3.10.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-source-plugin</artifactId>\n                    <version>3.3.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-javadoc-plugin</artifactId>\n                    <version>3.4.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-deploy-plugin</artifactId>\n                    <version>3.0.0</version>\n                </plugin>\n\n                <plugin>\n                    <groupId>org.springframework.boot</groupId>\n                    <artifactId>spring-boot-maven-plugin</artifactId>\n                    <version>${spring-boot.version}</version>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n    </build>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/start/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>${rootArtifactId}-parent</artifactId>\n        <version>${version}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${artifactId}</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>${groupId}</groupId>\n            <artifactId>${rootArtifactId}-app</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/start/src/main/java/Application.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package};\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * Spring Boot Starter\n *\n *\n * @author Frank Zhang\n */\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/start/src/main/resources/application.properties",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\nproject.name=${artifactId}\n\nspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver\nspring.datasource.url=jdbc:mysql://localhost:3306/test\nspring.datasource.username=root\nspring.datasource.password=root\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/start/src/main/resources/logback-spring.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n    <!--定义参数,后面可以通过${symbol_dollar}{APP_NAME}使用-->\n    <property name=\"APP_NAME\" value=\"${artifactId}\" />\n    <property name=\"LOG_PATH\" value=\"${symbol_dollar}{user.home}/${symbol_dollar}{APP_NAME}/logs\" />\n    <property name=\"LOG_FILE\" value=\"${symbol_dollar}{LOG_PATH}/application.log\" />\n\n    <appender name=\"APPLICATION\"\n        class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <!--定义日志输出的路径-->\n        <file>${symbol_dollar}{LOG_FILE}</file>\n        <encoder>\n            <pattern>${symbol_dollar}{FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${symbol_dollar}{LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>\n            <maxHistory>7</maxHistory>\n            <maxFileSize>50MB</maxFileSize>\n            <totalSizeCap>20GB</totalSizeCap>\n        </rollingPolicy>\n    </appender>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${symbol_dollar}{CONSOLE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <!--定义了两个appender，日志会通过往这两个appender里面写-->\n        <appender-ref ref=\"CONSOLE\" />\n        <appender-ref ref=\"APPLICATION\" />\n    </root>\n\n    <!--应用日志-->\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n    <logger name=\"${package}\" level=\"DEBUG\"/>\n\n    <!--数据库日志-->\n    <!--由于这个logger自动继承了root的appender，root中已经有stdout的appender了，自己这边又引入了stdout的appender-->\n    <!--如果没有设置 additivity=\"false\" ,就会导致一条日志在控制台输出两次的情况-->\n    <!--additivity表示要不要使用rootLogger配置的appender进行输出-->\n    <logger name=\"com.apache.ibatis\" level=\"TRACE\" additivity=\"false\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </logger>\n</configuration>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/start/src/test/java/TestApplication.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package};\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.context.ApplicationContext;\n\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        //这里填的是TestApplication\n        ApplicationContext context = SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/start/src/test/java/test/CustomerServiceTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.test;\n\nimport com.alibaba.cola.dto.Response;\nimport ${package}.api.CustomerServiceI;\nimport ${package}.dto.CustomerAddCmd;\nimport ${package}.dto.data.CustomerDTO;\nimport ${package}.dto.data.ErrorCode;\nimport ${package}.Application;\nimport org.junit.Assert;\nimport org.junit.Before;\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 * This is for integration test.\n *\n * Created by fulan.zjf on 2017/11/29.\n */\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class CustomerServiceTest {\n\n    @Autowired\n    private CustomerServiceI customerService;\n\n\n    @Before\n    public void setUp() {\n\n    }\n\n    @Test\n    public void testCustomerAddSuccess(){\n        //1.prepare\n        CustomerAddCmd customerAddCmd = new CustomerAddCmd();\n        CustomerDTO customerDTO = new CustomerDTO();\n        customerDTO.setCompanyName(\"NormalName\");\n        customerAddCmd.setCustomerDTO(customerDTO);\n\n        //2.execute\n        Response response = customerService.addCustomer(customerAddCmd);\n\n        //3.assert\n        Assert.assertTrue(response.isSuccess());\n    }\n\n    @Test\n    public void testCustomerAddCompanyNameConflict(){\n        //1.prepare\n        CustomerAddCmd customerAddCmd = new CustomerAddCmd();\n        CustomerDTO customerDTO = new CustomerDTO();\n        customerDTO.setCompanyName(\"ConflictCompanyName\");\n        customerAddCmd.setCustomerDTO(customerDTO);\n\n        //2.execute\n        Response response = customerService.addCustomer(customerAddCmd);\n\n        //3.Exception\n        Assert.assertEquals(ErrorCode.B_CUSTOMER_companyNameConflict, response.getErrCode());\n\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/start/src/test/resources/logback-test.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n\n    <!--应用日志-->\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n    <logger name=\"${package}\" level=\"DEBUG\"/>\n\n    <!--全局的访问日志-->\n    <logger name=\"com.alibaba.cola.catchlog\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/start/src/test/resources/test.properties",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n\n\n${symbol_pound}tddl\nspring.tddl.app=TDDL6_APP\nspring.tddl.sharding=false\nmybatis.config-location=classpath:mybatis/mybatis-config.xml\n\n${symbol_pound}app\nspring.hsf.group=HSF\nspring.hsf.version=1.0.0.DAILY\nspring.hsf.timeout=2000\n\n${symbol_pound}tair\nspring.tair.config-id=mdbcomm-daily\nspring.tair.dynamic-config=true\n\n${symbol_pound}notify\nspring.notify.subscriber.group-id=S-pb-test-subscriber1\nspring.notify.subscriber.messageListener=messageListener\nspring.notify.subscriber.binding.key=type\nspring.notify.subscriber.binding.topic=pandora-boot-test-topic\nspring.notify.publisher.group-id=P-pb-test\nspring.notify.publisher.topics=pandora-boot-test-topic2, pandora-boot-test-topic\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/test/resources/projects/basic/archetype.properties",
    "content": "#Tue Apr 16 18:52:28 CST 2019\npackage=it.pkg\nversion=0.1-SNAPSHOT\ngroupId=archetype.it\nartifactId=basic\ngitignore=.gitignore\n\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-service/src/test/resources/projects/basic/goal.txt",
    "content": ""
  },
  {
    "path": "cola-archetypes/cola-archetype-web/.gitignore",
    "content": "target/\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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    <parent>\n        <groupId>com.alibaba.cola</groupId>\n        <artifactId>cola-framework-archetypes-parent</artifactId>\n        <version>5.x-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>cola-framework-archetype-web</artifactId>\n    <packaging>maven-archetype</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/META-INF/maven/archetype-metadata.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archetype-descriptor\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd\"\n    name=\"demo\"\n    xmlns=\"http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0\">\n\n    <requiredProperties>\n        <requiredProperty key=\"gitignore\">\n            <defaultValue>.gitignore</defaultValue>\n        </requiredProperty>\n    </requiredProperties>\n\n    <fileSets>\n        <fileSet>\n            <directory></directory>\n            <includes>\n                <include>__gitignore__</include>\n            </includes>\n        </fileSet>\n    </fileSets>\n\n    <modules>\n        <module id=\"${rootArtifactId}-client\" dir=\"__rootArtifactId__-client\" name=\"${rootArtifactId}-client\">\n            <fileSets>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n            </fileSets>\n        </module>\n\n        <module id=\"${rootArtifactId}-adapter\" dir=\"__rootArtifactId__-adapter\" name=\"${rootArtifactId}-adapter\">\n            <fileSets>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n            </fileSets>\n        </module>\n\n        <module id=\"${rootArtifactId}-app\" dir=\"__rootArtifactId__-app\" name=\"${rootArtifactId}-app\">\n            <fileSets>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/test/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n            </fileSets>\n        </module>\n\n        <module id=\"${rootArtifactId}-domain\" dir=\"__rootArtifactId__-domain\" name=\"${rootArtifactId}-domain\">\n            <fileSets>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/test/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n            </fileSets>\n        </module>\n\n        <module id=\"${rootArtifactId}-infrastructure\" dir=\"__rootArtifactId__-infrastructure\"\n                name=\"${rootArtifactId}-infrastructure\">\n            <fileSets>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/resources</directory>\n                    <includes>\n                        <include>**/*.xml</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/test/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" encoding=\"UTF-8\">\n                    <directory>src/test/resources</directory>\n                    <includes>\n                        <include>**/*.properties</include>\n                    </includes>\n                </fileSet>\n            </fileSets>\n        </module>\n\n        <module id=\"start\" dir=\"start\" name=\"start\">\n            <fileSets>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" encoding=\"UTF-8\">\n                    <directory>src/main/resources</directory>\n                    <includes>\n                        <include>**/*.xml</include>\n                        <include>**/*.properties</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n                    <directory>src/test/java</directory>\n                    <includes>\n                        <include>**/*.java</include>\n                    </includes>\n                </fileSet>\n                <fileSet filtered=\"true\" encoding=\"UTF-8\">\n                    <directory>src/test/resources</directory>\n                    <includes>\n                        <include>**/*.xml</include>\n                        <include>**/*.properties</include>\n                    </includes>\n                </fileSet>\n            </fileSets>\n        </module>\n    </modules>\n</archetype-descriptor>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__gitignore__",
    "content": "target/\n\n### eclipse ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-adapter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>${rootArtifactId}-parent</artifactId>\n        <version>${version}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${artifactId}</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>${groupId}</groupId>\n            <artifactId>${rootArtifactId}-app</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-adapter/src/main/java/mobile/CustomerMobileAdaptor.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.mobile;\n\n/**\n * Customer Mobile Adaptor\n *\n *\n * @author Frank Zhang\n * @date 2020-10-27 8:04 PM\n */\npublic class CustomerMobileAdaptor {\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-adapter/src/main/java/wap/CustomerWapAdaptor.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.wap;\n\n/**\n * Customer Wap Adaptor\n *\n * WAP : Wireless Application Protocol)\n *\n * @author Frank Zhang\n * @date 2020-10-27 8:03 PM\n */\npublic class CustomerWapAdaptor {\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-adapter/src/main/java/web/CustomerController.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.web;\n\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.cola.dto.Response;\nimport ${package}.api.CustomerServiceI;\nimport ${package}.dto.CustomerAddCmd;\nimport ${package}.dto.CustomerListByNameQry;\nimport ${package}.dto.data.CustomerDTO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\npublic class CustomerController {\n\n    @Autowired\n    private CustomerServiceI customerService;\n\n    @GetMapping(value = \"/helloworld\")\n    public String helloWorld(){\n        return \"Hello, welcome to COLA world!\";\n    }\n\n    @GetMapping(value = \"/customer\")\n    public MultiResponse<CustomerDTO> listCustomerByName(@RequestParam(required = false) String name){\n        CustomerListByNameQry customerListByNameQry = new CustomerListByNameQry();\n        customerListByNameQry.setName(name);\n        return customerService.listByName(customerListByNameQry);\n    }\n\n    @PostMapping(value = \"/customer\")\n    public Response addCustomer(@RequestBody CustomerAddCmd customerAddCmd){\n        return customerService.addCustomer(customerAddCmd);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-app/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>${rootArtifactId}-parent</artifactId>\n        <version>${version}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${artifactId}</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>${groupId}</groupId>\n            <artifactId>${rootArtifactId}-client</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>${groupId}</groupId>\n            <artifactId>${rootArtifactId}-infrastructure</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-catchlog-starter</artifactId>\n        </dependency>\n\n        <!-- JSR 303 Validation -->\n        <dependency>\n            <groupId>org.hibernate.validator</groupId>\n            <artifactId>hibernate-validator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>javax.el</groupId>\n            <artifactId>javax.el-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.glassfish</groupId>\n            <artifactId>jakarta.el</artifactId>\n        </dependency>\n        <!-- JSR 303 Validation End-->\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-app/src/main/java/customer/CustomerServiceImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.customer;\n\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.catchlog.CatchAndLog;\nimport ${package}.api.CustomerServiceI;\nimport ${package}.dto.CustomerAddCmd;\nimport ${package}.dto.CustomerListByNameQry;\nimport ${package}.dto.data.CustomerDTO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport ${package}.customer.executor.CustomerAddCmdExe;\nimport ${package}.customer.executor.query.CustomerListByNameQryExe;\n\nimport javax.annotation.Resource;\n\n\n@Service\n@CatchAndLog\npublic class CustomerServiceImpl implements CustomerServiceI {\n\n    @Resource\n    private CustomerAddCmdExe customerAddCmdExe;\n\n    @Resource\n    private CustomerListByNameQryExe customerListByNameQryExe;\n\n    public Response addCustomer(CustomerAddCmd customerAddCmd) {\n        return customerAddCmdExe.execute(customerAddCmd);\n    }\n\n    @Override\n    public MultiResponse<CustomerDTO> listByName(CustomerListByNameQry customerListByNameQry) {\n        return customerListByNameQryExe.execute(customerListByNameQry);\n    }\n\n}"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-app/src/main/java/customer/executor/CustomerAddCmdExe.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n\npackage ${package}.customer.executor;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.exception.BizException;\nimport ${package}.dto.CustomerAddCmd;\nimport ${package}.dto.data.ErrorCode;\nimport org.springframework.stereotype.Component;\n\n\n@Component\npublic class CustomerAddCmdExe{\n\n    public Response execute(CustomerAddCmd cmd) {\n        //The flow of usecase is defined here.\n        //The core ablility should be implemented in Domain. or sink to Domian gradually\n        if(cmd.getCustomerDTO().getCompanyName().equals(\"ConflictCompanyName\")){\n            throw new BizException(ErrorCode.B_CUSTOMER_companyNameConflict.getErrCode(), \"公司名冲突\");\n        }\n        return Response.buildSuccess();\n    }\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-app/src/main/java/customer/executor/query/CustomerListByNameQryExe.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.customer.executor.query;\n\nimport com.alibaba.cola.dto.MultiResponse;\nimport ${package}.dto.CustomerListByNameQry;\nimport ${package}.dto.data.CustomerDTO;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.springframework.stereotype.Component;\n\n\n@Component\npublic class CustomerListByNameQryExe{\n    public MultiResponse<CustomerDTO> execute(CustomerListByNameQry cmd) {\n        List<CustomerDTO> customerDTOList = new ArrayList<>();\n        CustomerDTO customerDTO = new CustomerDTO();\n        customerDTO.setCustomerName(\"Frank\");\n        customerDTOList.add(customerDTO);\n        return MultiResponse.of(customerDTOList);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-app/src/main/java/order/OrderServiceImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.order;\n\n//package by domain, not by duty\n\n\npublic class OrderServiceImpl{\n\n}"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-app/src/test/java/app/CustomerConvertorTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.app;\n\n\npublic class CustomerConvertorTest {\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-app/src/test/java/app/CustomerValidatorTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.app;\n\nimport org.junit.Test;\n\npublic class CustomerValidatorTest {\n\n    @Test\n    public void testValidation(){\n\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>${rootArtifactId}-parent</artifactId>\n        <version>${version}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${parentArtifactId}-client</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-dto</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>javax.validation</groupId>\n            <artifactId>validation-api</artifactId>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/api/CustomerServiceI.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.api;\n\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.cola.dto.Response;\nimport ${package}.dto.CustomerAddCmd;\nimport ${package}.dto.CustomerListByNameQry;\nimport ${package}.dto.data.CustomerDTO;\n\npublic interface CustomerServiceI {\n\n    Response addCustomer(CustomerAddCmd customerAddCmd);\n\n    MultiResponse<CustomerDTO> listByName(CustomerListByNameQry customerListByNameQry);\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/dto/CustomerAddCmd.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.dto;\n\nimport ${package}.dto.data.CustomerDTO;\nimport lombok.Data;\n\n@Data\npublic class CustomerAddCmd{\n\n    private CustomerDTO customerDTO;\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/dto/CustomerListByNameQry.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.dto;\n\nimport com.alibaba.cola.dto.Query;\nimport lombok.Data;\n\n@Data\npublic class CustomerListByNameQry extends Query{\n   private String name;\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/dto/data/CustomerDTO.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.dto.data;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n@Data\npublic class CustomerDTO{\n    private String customerId;\n    private String memberId;\n    private String customerName;\n    private String customerType;\n    @NotEmpty\n    private String companyName;\n    @NotEmpty\n    private String source;\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/dto/data/ErrorCode.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.dto.data;\n\npublic enum ErrorCode{\n    B_CUSTOMER_companyNameConflict(\"B_CUSTOMER_companyNameConflict\", \"客户公司名冲突\");\n\n    private final String errCode;\n    private final String errDesc;\n\n    private ErrorCode(String errCode, String errDesc) {\n        this.errCode = errCode;\n        this.errDesc = errDesc;\n    }\n\n    public String getErrCode() {\n        return errCode;\n    }\n\n    public String getErrDesc() {\n        return errDesc;\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/dto/event/CustomerCreatedEvent.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.dto.event;\n\nimport static ${package}.dto.event.DomainEventConstant.CUSTOMER_CREATED_TOPIC;\n\n/**\n * CustomerCreatedEvent\n *\n * @author Frank Zhang\n * @date 2019-01-04 10:32 AM\n */\npublic class CustomerCreatedEvent{\n\n    private String customerId;\n\n    public String getCustomerId() {\n        return customerId;\n    }\n\n    public void setCustomerId(String customerId) {\n        this.customerId = customerId;\n    }\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/dto/event/DomainEventConstant.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.dto.event;\n\n/**\n * @author niexiaolong\n * @date 2019/4/16\n */\npublic class DomainEventConstant {\n\n\tpublic static final String CUSTOMER_CREATED_TOPIC = \"CRM_CUSTOMER_CREATED_DOMAIN_EVENT_TOPIC\";\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-domain/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>${rootArtifactId}-parent</artifactId>\n        <version>${version}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${artifactId}</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>${groupId}</groupId>\n            <artifactId>${rootArtifactId}-client</artifactId>\n        </dependency>\n\n        <!-- COLA components -->\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-domain-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-exception</artifactId>\n        </dependency>\n        <!-- COLA components End-->\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/CompanyType.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer;\n\n/**\n * CompanyType\n *\n * @author Frank Zhang\n * @date 2018-01-08 11:02 AM\n */\npublic enum CompanyType {\n    POTENTIAL,\n    INTENTIONAL,\n    IMPORTANT,\n    VIP;\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/Credit.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer;\n\nimport com.alibaba.cola.domain.Entity;\nimport lombok.Data;\n\n@Data\n@Entity\npublic class Credit{\n    \n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/Customer.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer;\n\nimport com.alibaba.cola.domain.Entity;\nimport com.alibaba.cola.exception.BizException;\nimport lombok.Data;\n\n//Domain Entity can choose to extend the domain model which is used for DTO\n@Data\n@Entity\npublic class Customer{\n\n    private String customerId;\n    private String memberId;\n    private String globalId;\n    private long registeredCapital;\n    private String companyName;\n    private SourceType sourceType;\n    private CompanyType companyType;\n\n    public Customer() {\n    }\n\n    public boolean isBigCompany() {\n        return registeredCapital > 10000000; //注册资金大于1000万的是大企业\n    }\n\n    public boolean isSME() {\n        return registeredCapital > 10000 && registeredCapital < 1000000; //注册资金大于10万小于100万的为中小企业\n    }\n\n    public void checkConflict(){\n        //Per different biz, the check policy could be different, if so, use ExtensionPoint\n        if(\"ConflictCompanyName\".equals(this.companyName)){\n            throw new BizException(this.companyName+\" has already existed, you can not add it\");\n        }\n\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/CustomerType.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer;\n\n/**\n * CustomerType\n *\n * @author Frank Zhang\n * @date 2018-01-08 8:51 AM\n */\npublic enum CustomerType {\n    POTENTIAL,\n    INTENTIONAL,\n    IMPORTANT,\n    VIP;\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/SourceType.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer;\n\n/**\n * SourceType\n *\n * @author Frank Zhang\n * @date 2018-01-08 11:09 AM\n */\npublic enum SourceType {\n    BIZ_ONE, //From biz one\n    BIZ_TWO; //From biz two\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/domainservice/CreditChecker.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer.domainservice;\n\n//The domain's ability can also be placed here\npublic class CreditChecker{\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/gateway/CreditGateway.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer.gateway;\n\nimport ${package}.domain.customer.Credit;\n\n//Assume that the credit info is in another distributed Service\npublic interface CreditGateway {\n    Credit getCredit(String customerId);\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/customer/gateway/CustomerGateway.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.customer.gateway;\n\nimport ${package}.domain.customer.Customer;\n\npublic interface CustomerGateway {\n    Customer getByById(String customerId);\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/order/Order.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain.order;\n\npublic class Order{\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/main/java/domain/package-info.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n/**\n * This is domain module, the core business logic is implemented here.\n * \n * @author fulan.zjf\n */\npackage ${package}.domain;"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-domain/src/test/java/domain/CustomerEntityTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.domain;\n\n\npublic class CustomerEntityTest {\n\n    public void testCustomerConflict() {\n        System.out.println(\"Please mock gatewayimpl, test pure Domain Knowledge\");\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>${rootArtifactId}-parent</artifactId>\n        <version>${version}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${artifactId}</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>${groupId}</groupId>\n            <artifactId>${rootArtifactId}-domain</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/java/config/DiamondConfig.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.config;\n\npublic class DiamondConfig {\n    public final static String DummyConfig = \"DummyConfig\";\n}"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/java/customer/CreditGatewayImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.customer;\n\nimport ${package}.domain.customer.Credit;\nimport ${package}.domain.customer.gateway.CreditGateway;\n\npublic class CreditGatewayImpl implements CreditGateway {\n    public Credit getCredit(String customerId){\n      return null;\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/java/customer/CustomerDO.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.customer;\n\nimport lombok.Data;\n\n@Data\npublic class CustomerDO{\n  private String customerId;\n  private String memberId;\n  private String globalId;\n  private long registeredCapital;\n  private String companyName;\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/java/customer/CustomerGatewayImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.customer;\n\nimport ${package}.domain.customer.Customer;\nimport ${package}.domain.customer.gateway.CustomerGateway;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CustomerGatewayImpl implements CustomerGateway {\n    @Autowired\n    private CustomerMapper customerMapper;\n\n    public Customer getByById(String customerId){\n      CustomerDO customerDO = customerMapper.getById(customerId);\n      //Convert to Customer\n      return null;\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/java/customer/CustomerMapper.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.customer;\n\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface CustomerMapper{\n\n  CustomerDO getById(String customerId);\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/java/order/OrderGatewayImpl.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.order;\n\npublic class OrderGatewayImpl{\n\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/resources/logback-spring.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <!-- https://github.com/spring-projects/spring-boot/blob/v1.4.2.RELEASE/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n    <property name=\"APP_NAME\" value=\"${parentArtifactId}\" />\n    <property name=\"LOG_PATH\" value=\"${symbol_dollar}{user.home}/${symbol_dollar}{APP_NAME}/logs\" />\n    <property name=\"LOG_FILE\" value=\"${symbol_dollar}{LOG_PATH}/application.log\" />\n\n    <appender name=\"APPLICATION\"\n        class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${symbol_dollar}{LOG_FILE}</file>\n        <encoder>\n            <pattern>${symbol_dollar}{FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${symbol_dollar}{LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>\n            <maxHistory>7</maxHistory>\n            <maxFileSize>50MB</maxFileSize>\n            <totalSizeCap>20GB</totalSizeCap>\n        </rollingPolicy>\n    </appender>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${symbol_dollar}{CONSOLE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\" />\n        <appender-ref ref=\"APPLICATION\" />\n    </root>\n</configuration>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/resources/mybatis/customer-mapper.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"${package}.datatunnel.CustomerTunnelI\">\n    <select id=\"findByCriteria\" resultType=\"${package}.database.dataobject.CustomerDO\" resultMap=\"customerMap\">\n        select * from customer where customer_id = ${symbol_pound}{id}\n    </select>\n    <resultMap type=\"${package}.database.dataobject.CustomerDO\" id=\"customerMap\">\n         <result property=\"customerId\" column=\"emp_no\"/>\n         <result property=\"memberId\" column=\"salary\"/>\n         <result property=\"globalId\" column=\"from_date\"/>\n     </resultMap>\n</mapper>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/main/resources/mybatis/mybatis-config.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!-- mybatis的配置文件 -->\n<!DOCTYPE configuration\n        PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\t<mappers>\n        <mapper resource=\"mybatis/customer-mapper.xml\"/>\n    </mappers>\n</configuration>"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/test/java/repository/CustomerMapperTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.repository;\n\n\npublic class CustomerMapperTest {\n\n    public void testFindByID() {\n        System.out.println(\"Write your test here\");\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/__rootArtifactId__-infrastructure/src/test/resources/sample.properties",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>${groupId}</groupId>\n    <artifactId>${artifactId}-parent</artifactId>\n    <version>${version}</version>\n    <packaging>pom</packaging>\n    <name>${artifactId}</name>\n\n    <properties>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <maven.compiler.target>${maven.compiler.source}</maven.compiler.target>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <maven.deploy.skip>true</maven.deploy.skip>\n\n        <cola.components.version>5.x-SNAPSHOT</cola.components.version>\n\n        <spring-boot.version>2.7.2</spring-boot.version>\n        <mybatis-starter.version>2.2.2</mybatis-starter.version>\n    </properties>\n\n    <modules>\n        <module>${rootArtifactId}-client</module>\n        <module>${rootArtifactId}-adapter</module>\n        <module>${rootArtifactId}-app</module>\n        <module>${rootArtifactId}-domain</module>\n        <module>${rootArtifactId}-infrastructure</module>\n        <module>start</module>\n    </modules>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <!--Project modules-->\n            <dependency>\n                <groupId>${groupId}</groupId>\n                <artifactId>${rootArtifactId}-adapter</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>${groupId}</groupId>\n                <artifactId>${rootArtifactId}-client</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>${groupId}</groupId>\n                <artifactId>${rootArtifactId}-app</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>${groupId}</groupId>\n                <artifactId>${rootArtifactId}-domain</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>${groupId}</groupId>\n                <artifactId>${rootArtifactId}-infrastructure</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <!--Project modules End-->\n\n            <dependency>\n                <groupId>com.alibaba.cola</groupId>\n                <artifactId>cola-components-bom</artifactId>\n                <version>${cola.components.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-dependencies</artifactId>\n                <version>${spring-boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.mybatis.spring.boot</groupId>\n                <artifactId>mybatis-spring-boot-starter</artifactId>\n                <version>${mybatis-starter.version}</version>\n            </dependency>\n\n            <!--Validation API-->\n            <!--\n                - javax.validation:javax.validation\n                - org.hibernate.validator:hibernate-validator\n                - org.glassfish:jakarta.el\n                are declared in spring-boot-dependencies\n            -->\n            <dependency>\n                <groupId>javax.el</groupId>\n                <artifactId>javax.el-api</artifactId>\n                <version>3.0.0</version>\n            </dependency>\n            <!--Validation API End -->\n\n            <!-- Misc -->\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>fastjson</artifactId>\n                <version>1.2.83</version>\n            </dependency>\n            <!-- Misc End -->\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <artifactId>maven-resources-plugin</artifactId>\n                    <version>3.3.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-compiler-plugin</artifactId>\n                    <version>3.10.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-source-plugin</artifactId>\n                    <version>3.3.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-javadoc-plugin</artifactId>\n                    <version>3.4.0</version>\n                </plugin>\n                <plugin>\n                     <artifactId>maven-deploy-plugin</artifactId>\n                    <version>3.0.0</version>\n                </plugin>\n\n                <plugin>\n                    <groupId>org.springframework.boot</groupId>\n                    <artifactId>spring-boot-maven-plugin</artifactId>\n                    <version>${spring-boot.version}</version>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n    </build>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/start/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>${rootArtifactId}-parent</artifactId>\n        <version>${version}</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${artifactId}</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>${groupId}</groupId>\n            <artifactId>${rootArtifactId}-adapter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/start/src/main/java/Application.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package};\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * Spring Boot Starter\n *\n * @author Frank Zhang\n */\n@SpringBootApplication(scanBasePackages = {\"${package}\", \"com.alibaba.cola\"})\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/start/src/main/resources/application.properties",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\nproject.name=${artifactId}\n\nspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver\nspring.datasource.url=jdbc:mysql://localhost:3306/test\nspring.datasource.username=root\nspring.datasource.password=root\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/start/src/main/resources/logback-spring.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n    <!--定义参数,后面可以通过${symbol_dollar}{APP_NAME}使用-->\n    <property name=\"APP_NAME\" value=\"${artifactId}\" />\n    <property name=\"LOG_PATH\" value=\"${symbol_dollar}{user.home}/${symbol_dollar}{APP_NAME}/logs\" />\n    <property name=\"LOG_FILE\" value=\"${symbol_dollar}{LOG_PATH}/application.log\" />\n\n    <appender name=\"APPLICATION\"\n        class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <!--定义日志输出的路径-->\n        <file>${symbol_dollar}{LOG_FILE}</file>\n        <encoder>\n            <pattern>${symbol_dollar}{FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${symbol_dollar}{LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>\n            <maxHistory>7</maxHistory>\n            <maxFileSize>50MB</maxFileSize>\n            <totalSizeCap>20GB</totalSizeCap>\n        </rollingPolicy>\n    </appender>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${symbol_dollar}{CONSOLE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <!--定义了两个appender，日志会通过往这两个appender里面写-->\n        <appender-ref ref=\"CONSOLE\" />\n        <appender-ref ref=\"APPLICATION\" />\n    </root>\n\n    <!--应用日志-->\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n    <logger name=\"${package}\" level=\"DEBUG\"/>\n\n    <!--数据库日志-->\n    <!--由于这个logger自动继承了root的appender，root中已经有stdout的appender了，自己这边又引入了stdout的appender-->\n    <!--如果没有设置 additivity=\"false\" ,就会导致一条日志在控制台输出两次的情况-->\n    <!--additivity表示要不要使用rootLogger配置的appender进行输出-->\n    <logger name=\"com.apache.ibatis\" level=\"TRACE\" additivity=\"false\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </logger>\n</configuration>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/start/src/test/java/TestApplication.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package};\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.context.ApplicationContext;\n\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/start/src/test/java/test/CustomerServiceTest.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.test;\n\nimport com.alibaba.cola.dto.Response;\nimport ${package}.api.CustomerServiceI;\nimport ${package}.dto.CustomerAddCmd;\nimport ${package}.dto.data.CustomerDTO;\nimport ${package}.dto.data.ErrorCode;\nimport org.junit.Assert;\nimport org.junit.Before;\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 * This is for integration test.\n *\n * Created by fulan.zjf on 2017/11/29.\n */\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class CustomerServiceTest {\n\n    @Autowired\n    private CustomerServiceI customerService;\n\n\n    @Before\n    public void setUp() {\n\n    }\n\n    @Test\n    public void testCustomerAddSuccess(){\n        //1.prepare\n        CustomerAddCmd customerAddCmd = new CustomerAddCmd();\n        CustomerDTO customerDTO = new CustomerDTO();\n        customerDTO.setCompanyName(\"NormalName\");\n        customerAddCmd.setCustomerDTO(customerDTO);\n\n        //2.execute\n        Response response = customerService.addCustomer(customerAddCmd);\n\n        //3.assert\n        Assert.assertTrue(response.isSuccess());\n    }\n\n    @Test\n    public void testCustomerAddCompanyNameConflict(){\n        //1.prepare\n        CustomerAddCmd customerAddCmd = new CustomerAddCmd();\n        CustomerDTO customerDTO = new CustomerDTO();\n        customerDTO.setCompanyName(\"ConflictCompanyName\");\n        customerAddCmd.setCustomerDTO(customerDTO);\n\n        //2.execute\n        Response response = customerService.addCustomer(customerAddCmd);\n\n        //3.assert error\n        Assert.assertEquals(ErrorCode.B_CUSTOMER_companyNameConflict.getErrCode(), response.getErrCode());\n    }\n}\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/start/src/test/resources/logback-test.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n    <!--定义参数,后面可以通过${symbol_dollar}{APP_NAME}使用-->\n    <property name=\"APP_NAME\" value=\"${artifactId}\" />\n    <property name=\"LOG_PATH\" value=\"${symbol_dollar}{user.home}/${symbol_dollar}{APP_NAME}/logs\" />\n    <property name=\"LOG_FILE\" value=\"${symbol_dollar}{LOG_PATH}/application.log\" />\n\n    <appender name=\"APPLICATION\"\n        class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <!--定义日志输出的路径-->\n        <file>${symbol_dollar}{LOG_FILE}</file>\n        <encoder>\n            <pattern>${symbol_dollar}{FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${symbol_dollar}{LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>\n            <maxHistory>7</maxHistory>\n            <maxFileSize>50MB</maxFileSize>\n            <totalSizeCap>20GB</totalSizeCap>\n        </rollingPolicy>\n    </appender>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${symbol_dollar}{CONSOLE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <!--定义了两个appender，日志会通过往这两个appender里面写-->\n        <appender-ref ref=\"CONSOLE\" />\n        <appender-ref ref=\"APPLICATION\" />\n    </root>\n\n    <!--应用日志-->\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n    <logger name=\"${package}\" level=\"DEBUG\"/>\n\n    <!--数据库日志-->\n    <!--由于这个logger自动继承了root的appender，root中已经有stdout的appender了，自己这边又引入了stdout的appender-->\n    <!--如果没有设置 additivity=\"false\" ,就会导致一条日志在控制台输出两次的情况-->\n    <!--additivity表示要不要使用rootLogger配置的appender进行输出-->\n    <logger name=\"com.apache.ibatis\" level=\"TRACE\" additivity=\"false\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </logger>\n</configuration>\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/start/src/test/resources/test.properties",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n\n\n${symbol_pound}tddl\nspring.tddl.app=TDDL6_APP\nspring.tddl.sharding=false\nmybatis.config-location=classpath:mybatis/mybatis-config.xml\n\n${symbol_pound}app\nspring.hsf.group=HSF\nspring.hsf.version=1.0.0.DAILY\nspring.hsf.timeout=2000\n\n${symbol_pound}tair\nspring.tair.config-id=mdbcomm-daily\nspring.tair.dynamic-config=true\n\n${symbol_pound}notify\nspring.notify.subscriber.group-id=S-pb-test-subscriber1\nspring.notify.subscriber.messageListener=messageListener\nspring.notify.subscriber.binding.key=type\nspring.notify.subscriber.binding.topic=pandora-boot-test-topic\nspring.notify.publisher.group-id=P-pb-test\nspring.notify.publisher.topics=pandora-boot-test-topic2, pandora-boot-test-topic\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/test/resources/projects/basic/archetype.properties",
    "content": "#Tue Apr 16 18:52:28 CST 2019\npackage=it.pkg\nversion=0.1-SNAPSHOT\ngroupId=archetype.it\nartifactId=basic\ngitignore=.gitignore\n"
  },
  {
    "path": "cola-archetypes/cola-archetype-web/src/test/resources/projects/basic/goal.txt",
    "content": ""
  },
  {
    "path": "cola-archetypes/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.alibaba.cola</groupId>\n    <artifactId>cola-framework-archetypes-parent</artifactId>\n    <version>5.x-SNAPSHOT</version>\n    <packaging>pom</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n\n    <modules>\n        <module>cola-archetype-light</module>\n        <module>cola-archetype-service</module>\n        <module>cola-archetype-web</module>\n    </modules>\n\n    <properties>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <maven.compiler.target>${maven.compiler.source}</maven.compiler.target>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    </properties>\n\n    <build>\n        <extensions>\n            <extension>\n                <groupId>org.apache.maven.archetype</groupId>\n                <artifactId>archetype-packaging</artifactId>\n                <version>3.2.1</version>\n            </extension>\n        </extensions>\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <artifactId>maven-archetype-plugin</artifactId>\n                    <version>3.2.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-resources-plugin</artifactId>\n                    <version>3.3.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-compiler-plugin</artifactId>\n                    <version>3.13.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-source-plugin</artifactId>\n                    <version>3.3.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-javadoc-plugin</artifactId>\n                    <version>3.7.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-gpg-plugin</artifactId>\n                    <version>3.1.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-jar-plugin</artifactId>\n                    <version>3.3.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-surefire-plugin</artifactId>\n                    <version>3.2.5</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-deploy-plugin</artifactId>\n                    <version>3.1.1</version>\n                </plugin>\n                <plugin>\n                    <groupId>org.sonatype.plugins</groupId>\n                    <artifactId>nexus-staging-maven-plugin</artifactId>\n                    <version>1.6.13</version>\n                </plugin>\n                <plugin>\n                    <groupId>org.jacoco</groupId>\n                    <artifactId>jacoco-maven-plugin</artifactId>\n                    <version>0.8.12</version>\n                </plugin>\n                <plugin>\n                    <groupId>pl.project13.maven</groupId>\n                    <artifactId>git-commit-id-plugin</artifactId>\n                    <version>4.9.10</version>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n    </build>\n\n    <distributionManagement>\n        <snapshotRepository>\n            <id>ossrh</id>\n            <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n        </snapshotRepository>\n    </distributionManagement>\n\n    <profiles>\n        <profile>\n            <id>gen-java-src</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <artifactId>maven-source-plugin</artifactId>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>gen-java-doc</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <artifactId>maven-javadoc-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <id>attach-javadoc</id>\n                                <goals>\n                                    <goal>jar</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                        <configuration>\n                            <source>8</source>\n                            <show>protected</show>\n                            <charset>UTF-8</charset>\n                            <encoding>UTF-8</encoding>\n                            <docencoding>UTF-8</docencoding>\n                            <additionalJOptions>\n                                <additionalJOption>-quiet</additionalJOption>\n                                <additionalJOption>-J-Duser.language=en</additionalJOption>\n                                <additionalJOption>-J-Duser.country=US</additionalJOption>\n                                <additionalJOption>-Xdoclint:none</additionalJOption>\n                            </additionalJOptions>\n                        </configuration>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>gen-sign</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <artifactId>maven-gpg-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <id>sign-artifacts</id>\n                                <phase>verify</phase>\n                                <goals>\n                                    <goal>sign</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>gen-git-properties</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <!--\n                        Maven plugin which includes build-time git repository information into an POJO / *.properties).\n                        Make your apps tell you which version exactly they were built from! Priceless in large distributed deployments.\n                            https://github.com/ktoso/maven-git-commit-id-plugin\n                    -->\n                    <plugin>\n                        <groupId>pl.project13.maven</groupId>\n                        <artifactId>git-commit-id-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <id>get-the-git-infos</id>\n                                <goals>\n                                    <goal>revision</goal>\n                                </goals>\n                            </execution>\n                            <execution>\n                                <id>validate-the-git-infos</id>\n                                <goals>\n                                    <goal>validateRevision</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                        <configuration>\n                            <validationProperties>\n                                <!-- verify that the current repository is not dirty -->\n                                <validationProperty>\n                                    <name>validating git dirty</name>\n                                    <value>${git.dirty}</value>\n                                    <shouldMatchTo>false</shouldMatchTo>\n                                </validationProperty>\n                            </validationProperties>\n                            <generateGitPropertiesFile>true</generateGitPropertiesFile>\n                            <generateGitPropertiesFilename>\n                                ${project.build.outputDirectory}/META-INF/scm/${project.groupId}/${project.artifactId}/git.properties\n                            </generateGitPropertiesFilename>\n                        </configuration>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>force-jdk11-when-release</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <!--\n                        add maven-enforce-plugin to make sure the right jdk is used\n                        https://stackoverflow.com/a/18420462/922688\n                    -->\n                    <plugin>\n                        <artifactId>maven-enforcer-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <id>enforce-jdk-versions</id>\n                                <goals>\n                                    <goal>enforce</goal>\n                                </goals>\n                                <configuration>\n                                    <rules>\n                                        <requireJavaVersion>\n                                            <version>11</version>\n                                        </requireJavaVersion>\n                                    </rules>\n                                </configuration>\n                            </execution>\n                        </executions>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>deploy-settings</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.sonatype.plugins</groupId>\n                        <artifactId>nexus-staging-maven-plugin</artifactId>\n                        <extensions>true</extensions>\n                        <configuration>\n                            <serverId>ossrh</serverId>\n                            <nexusUrl>https://oss.sonatype.org/</nexusUrl>\n                            <autoReleaseAfterClose>true</autoReleaseAfterClose>\n                        </configuration>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n    </profiles>\n</project>\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/.gitignore",
    "content": "target/\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/README.md",
    "content": "## 原理\n通过注解AOP，提供service级别的logging和exception处理。\n\n通过Spring Boot的autoConfig机制进行加载，无需手动配置，只需要添加如下依赖即可：\n```xml\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-catchlog-starter</artifactId>\n        </dependency>\n```\n\n兼容普通的HSF service和Mtop service，具体做法可以查看代码`ResponseHandler`\n```java\npublic class ResponseHandler {\n\n    public static Object handle(Class returnType, String errCode, String errMsg){\n        if (isColaResponse(returnType)){\n            return handleColaResponse(returnType, errCode, errMsg);\n        }\n        if(isMtopResponse(returnType)){\n            return handleMtopResponse(returnType, errCode, errMsg);\n        }\n        return null;\n    }\n    ...\n  }\n\n```\n\n\n## 使用介绍\n1、在需要处理的Service类上面加上@CatchAndLog注解\n```java\n@CatchAndLog\npublic class GrouponServiceImpl implements GrouponService \n```\n\n2、logback-test.xml为组件开启DEGUG level的日志输出\n```xml\n   <!--这个是统一异常处理，日志记录组件的日志-->\n    <logger name=\"com.alibaba.cola.catchlog\" level=\"DEBUG\"/>\n```\n\n3、如果在控制台看到如下的日志输出，说明CatchAndLog已经在做AOP拦截\n```xml\nDEBUG c.a.c.catchlog.CatchLogAspect - Start processing: GrouponServiceImpl.queryGrouponItemDetail(..)\nDEBUG c.a.c.catchlog.CatchLogAspect - REQUEST : 257\n\nDEBUG c.a.c.catchlog.CatchLogAspect - RESPONSE : {\"errCode\":\"UNKNOWN_ERROR\"...}\nDEBUG c.a.c.catchlog.CatchLogAspect - COST : 1329ms\n```\n\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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    <parent>\n        <groupId>com.alibaba.cola</groupId>\n        <artifactId>cola-components-parent</artifactId>\n        <version>5.x-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>cola-component-catchlog-starter</artifactId>\n    <packaging>jar</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-autoconfigure</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-configuration-processor</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n\n        <!-- 工具包 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n\n        <!-- cola组件依赖 -->\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-dto</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-exception</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n        <!-- 测试包 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/main/java/com/alibaba/cola/catchlog/ApplicationContextHelper.java",
    "content": "package com.alibaba.cola.catchlog;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.stereotype.Component;\n\n/**\n * ApplicationContextHelper\n *\n * @author Frank Zhang\n * @date 2020-11-14 1:58 PM\n */\n@Component(\"colaCatchLogApplicationContextHelper\")\n@Slf4j\npublic class ApplicationContextHelper implements ApplicationContextAware {\n    private static ApplicationContext applicationContext;\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        ApplicationContextHelper.applicationContext = applicationContext;\n    }\n\n    public static <T> T getBean(Class<T> targetClz) {\n        T beanInstance = null;\n        //优先按type查\n        try {\n            beanInstance = (T)applicationContext.getBean(targetClz);\n        } catch (Exception e) {\n        }\n\n        //按name查\n        try {\n            if (beanInstance == null) {\n                String simpleName = targetClz.getSimpleName();\n                //首字母小写\n                simpleName = Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);\n                beanInstance = (T) applicationContext.getBean(simpleName);\n            }\n        }\n        catch (Exception e) {\n            log.warn(\"No bean found for \" + targetClz.getCanonicalName());\n        }\n        return beanInstance;\n    }\n\n    public static Object getBean(String claz) {\n        return ApplicationContextHelper.applicationContext.getBean(claz);\n    }\n\n    public static <T> T getBean(String name, Class<T> requiredType) {\n        return ApplicationContextHelper.applicationContext.getBean(name, requiredType);\n    }\n\n    public static <T> T getBean(Class<T> requiredType, Object... params) {\n        return ApplicationContextHelper.applicationContext.getBean(requiredType, params);\n    }\n\n    public static ApplicationContext getApplicationContext() {\n        return applicationContext;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/main/java/com/alibaba/cola/catchlog/CatchAndLog.java",
    "content": "package com.alibaba.cola.catchlog;\n\n/**\n * CatchAndLog\n *\n * @author Frank Zhang\n * @date 2020-11-10 10:48 AM\n */\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target({ElementType.METHOD, ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface CatchAndLog {\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/main/java/com/alibaba/cola/catchlog/CatchLogAspect.java",
    "content": "package com.alibaba.cola.catchlog;\n\nimport com.alibaba.cola.exception.BizException;\nimport com.alibaba.cola.exception.SysException;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.serializer.SerializerFeature;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Pointcut;\nimport org.aspectj.lang.reflect.MethodSignature;\nimport org.springframework.core.annotation.Order;\n\n/**\n * @Description   :  Catching and Logging\n * @author        :  Frank Zhang\n * @CreateDate    :  2020/11/09\n * @version       :  1.0\n */\n@Aspect\n@Slf4j\n@Order(1)\npublic class CatchLogAspect {\n\n    /**\n     *  <a href=\"https://blog.csdn.net/zhengchao1991/article/details/53391244\">The syntax of pointcut </a>\n     */\n    @Pointcut(\"@within(CatchAndLog) && execution(public * *(..))\")\n    public void pointcut() {\n    }\n\n    @Around(value = \"pointcut()\")\n    public Object around(ProceedingJoinPoint joinPoint) {\n        long startTime = System.currentTimeMillis();\n\n        logRequest(joinPoint);\n\n        Object response = null;\n        try {\n            response = joinPoint.proceed();\n        } catch (Throwable e) {\n            response = handleException(joinPoint, e);\n        } finally {\n            logResponse(startTime, response);\n        }\n\n        return response;\n    }\n\n    private Object handleException(ProceedingJoinPoint joinPoint, Throwable e) {\n        MethodSignature ms = (MethodSignature) joinPoint.getSignature();\n        Class<?> returnType = ms.getReturnType();\n\n        if (e instanceof BizException) {\n            log.warn(\"BIZ EXCEPTION : {}\", e.getMessage());\n            // 在Debug的时候，对于BizException也打印堆栈\n            if (log.isDebugEnabled()) {\n                log.error(e.getMessage(), e);\n            }\n            return ResponseHandlerFactory.get().handle(returnType,\n                ((BizException) e).getErrCode(), e.getMessage());\n        } else if (e instanceof SysException) {\n            log.error(\"SYS EXCEPTION: {}\",e.getMessage(), e);\n          return ResponseHandlerFactory.get().handle(returnType,\n              ((SysException) e).getErrCode(), e.getMessage());\n        }\n\n        log.error(\"UNKNOWN EXCEPTION: {}\", e.getMessage(), e);\n        return ResponseHandlerFactory.get().handle(returnType, \"UNKNOWN_ERROR\", e.getMessage());\n    }\n\n\n    private void logResponse(long startTime, Object response) {\n        try {\n            long endTime = System.currentTimeMillis();\n            if (log.isDebugEnabled()) {\n                log.debug(\"RESPONSE: {}\", JSON.toJSONString(response));\n                log.debug(\"COST: {}ms\", (endTime - startTime));\n            }\n        } catch (Exception e) {\n            //swallow it\n            log.error(\"logResponse error: {}\", e.getMessage() , e);\n        }\n    }\n\n    private void logRequest(ProceedingJoinPoint joinPoint) {\n      if (!log.isDebugEnabled()) {\n        return;\n      }\n\n      try {\n            log.debug(\"START PROCESSING: {}\", joinPoint.getSignature().toShortString());\n            Object[] args = joinPoint.getArgs();\n            for (Object arg : args) {\n                log.debug(\"REQUEST: {}\", JSON.toJSONString(arg, SerializerFeature.IgnoreErrorGetter));\n            }\n        } catch (Exception e) {\n            //swallow it\n            log.error(\"logReqeust error: {}\", e.getMessage() , e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/main/java/com/alibaba/cola/catchlog/CatchLogAutoConfiguration.java",
    "content": "package com.alibaba.cola.catchlog;\n\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n/**\n * @ Description   :\n * @ Author        :  Frank Zhang\n * @ CreateDate    :  2020/11/09\n * @ Version       :  1.0\n */\n@Configuration\n@EnableAspectJAutoProxy\npublic class CatchLogAutoConfiguration {\n\n    @Bean\n    @ConditionalOnMissingBean(CatchLogAspect.class)\n    public CatchLogAspect catchLogAspect() {\n        return new CatchLogAspect();\n    }\n\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/main/java/com/alibaba/cola/catchlog/DefaultResponseHandler.java",
    "content": "package com.alibaba.cola.catchlog;\n\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.exception.BaseException;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Component;\n\n/**\n * ResponseHandler\n *\n * @author Frank Zhang\n * @date 2020-11-10 3:18 PM\n */\n@Slf4j\npublic class DefaultResponseHandler implements ResponseHandlerI{\n\n    @Override\n    public  Object handle(Class returnType, String errCode, String errMsg){\n        if (isColaResponse(returnType)){\n            return handleColaResponse(returnType, errCode, errMsg);\n        }\n        return null;\n    }\n\n    public  Object handle(Class returnType, BaseException e){\n        return handle(returnType, e.getErrCode(), e.getMessage());\n    }\n\n\n    private static Object handleColaResponse(Class returnType, String errCode, String errMsg) {\n        try {\n            Response response = (Response)returnType.newInstance();\n            response.setSuccess(false);\n            response.setErrCode(errCode);\n            response.setErrMessage(errMsg);\n            return response;\n        }\n        catch (Exception ex){\n            log.error(ex.getMessage(), ex);\n            return  null;\n        }\n    }\n\n    private static boolean isColaResponse(Class returnType) {\n        return  returnType == Response.class || returnType.getGenericSuperclass() == Response.class;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/main/java/com/alibaba/cola/catchlog/ResponseHandlerFactory.java",
    "content": "package com.alibaba.cola.catchlog;\n\nimport org.springframework.context.annotation.Bean;\n\npublic class ResponseHandlerFactory {\n\n    public static ResponseHandlerI get(){\n        if(ApplicationContextHelper.getBean(ResponseHandlerI.class) != null){\n            return ApplicationContextHelper.getBean(ResponseHandlerI.class);\n        }\n        return new DefaultResponseHandler();\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/main/java/com/alibaba/cola/catchlog/ResponseHandlerI.java",
    "content": "package com.alibaba.cola.catchlog;\n\nimport com.alibaba.cola.exception.BaseException;\n\npublic interface ResponseHandlerI {\n    public Object handle(Class returnType, String errCode, String errMsg);\n}\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.boot.autoconfigure.EnableAutoConfiguration = com.alibaba.cola.catchlog.CatchLogAutoConfiguration\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/test/java/com/alibaba/cola/catchlog/test/Application.java",
    "content": "package com.alibaba.cola.catchlog.test;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * Application\n *\n * @author Frank Zhang\n * @date 2020-11-10 3:58 PM\n */\n@SpringBootApplication(scanBasePackages = {\"com.alibaba.cola.catchlog\"})\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/test/java/com/alibaba/cola/catchlog/test/CatchLogTest.java",
    "content": "package com.alibaba.cola.catchlog.test;\n\nimport com.alibaba.cola.catchlog.CatchLogAspect;\nimport com.alibaba.cola.catchlog.CatchLogAutoConfiguration;\nimport jakarta.annotation.Resource;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n/**\n *\n */\n@SpringBootTest(classes = {CatchLogAutoConfiguration.class, Demo.class, CatchLogAspect.class, Application.class})\npublic class CatchLogTest {\n\n    @Resource\n    private Demo demo;\n\n    @Test\n    public void testAspect() {\n        demo.doSomething();\n    }\n\n    @Test\n    public void testCatchAndLog() {\n        Demo.Request request = new Demo.Request();\n        request.name = \"Frank\";\n        request.age = 18;\n        demo.execute(request);\n    }\n\n    @Test\n    public void testExecuteWithResponse(){\n        Demo.Request request = new Demo.Request();\n        request.name = \"Frank\";\n        request.age = 18;\n        demo.executeWithResponse(request);\n    }\n\n    @Test\n    public void testExecuteWithVoid(){\n        demo.executeWithVoid();\n    }\n\n    @Test\n    public void testExecuteWithExceptionAndVoid(){\n        demo.executeWithExceptionAndVoid();\n    }\n\n    @Test\n    public void testExecuteWithExceptionAndDemoResponse(){\n        demo.executeWithExceptionAndDemoResponse();\n    }\n\n    @Test\n    public void testExecuteWithBizExceptionAndResponse(){\n        demo.executeWithBizExceptionAndResponse();\n    }\n\n    @Test\n    public void testExecuteWithSysExceptionAndResponse(){\n        demo.executeWithSysExceptionAndResponse();\n    }\n\n    @Test\n    public void testExecuteWithExceptionAndResponse(){\n        demo.executeWithExceptionAndResponse();\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/test/java/com/alibaba/cola/catchlog/test/CustomResponseHandler.java",
    "content": "package com.alibaba.cola.catchlog.test;\n\nimport com.alibaba.cola.catchlog.ResponseHandlerI;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CustomResponseHandler implements ResponseHandlerI{\n\n    @Override\n    public Object handle(Class returnType, String errCode, String errMsg) {\n        System.out.println(\"==== This is Customized Response handler\");\n        Demo.DemoResponse response = new Demo.DemoResponse();\n        response.setSuccess(false);\n        return response;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/test/java/com/alibaba/cola/catchlog/test/Demo.java",
    "content": "package com.alibaba.cola.catchlog.test;\n\nimport com.alibaba.cola.catchlog.CatchAndLog;\nimport com.alibaba.cola.catchlog.CatchLogAspect;\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.dto.SingleResponse;\nimport com.alibaba.cola.exception.BizException;\nimport com.alibaba.cola.exception.SysException;\nimport lombok.AllArgsConstructor;\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * Demo\n *\n * @author Frank Zhang\n * @date 2020-11-10 11:19 AM\n */\n@Configuration\n@CatchAndLog\npublic class Demo implements ApplicationContextAware {\n    static ApplicationContext applicationContext;\n\n    public void doSomething(){\n        System.out.println(\"Doing something\");\n        CatchLogAspect catchAndLog = applicationContext.getBean(CatchLogAspect.class);\n        System.out.println(catchAndLog);\n        doSomethingInner();\n    }\n\n    private void doSomethingInner(){\n        System.out.println(\"doSomethingInner\");\n    }\n\n    public DemoResponse execute(Request request){\n        System.out.println(\"executing request\");\n        return new DemoResponse(request.name, true);\n    }\n\n    public DemoResponse executeWithExceptionAndDemoResponse(){\n        if(true){\n            throw new RuntimeException(\"executeWithExceptionAndDemoResponse\");\n        }\n        return null;\n    }\n\n    public Response executeWithResponse(Request request){\n        DemoResponse demoResponse = new DemoResponse(\"Jack Ma\", true);\n        return SingleResponse.of(demoResponse);\n    }\n\n    public Response executeWithExceptionAndResponse(){\n        if(true){\n            throw new RuntimeException(\"execute With Exception And Response\");\n        }\n        return null;\n    }\n\n    public void executeWithVoid(){\n        System.out.println(\"execute with void\");\n    }\n\n    public void executeWithExceptionAndVoid(){\n        if(true){\n            throw new BizException(\"execute With Exception And Void\");\n        }\n    }\n\n    public Response executeWithBizExceptionAndResponse(){\n        if(true){\n            throw new BizException(\"execute With BizException And Response\");\n        }\n        return null;\n    }\n\n    public Response executeWithSysExceptionAndResponse(){\n        if(true){\n            throw new SysException(\"execute With SysException And Response\");\n        }\n        return null;\n    }\n\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        this.applicationContext = applicationContext;\n    }\n\n    public static class Request {\n        public String name;\n        public int  age;\n    }\n\n    @AllArgsConstructor\n    public static class DemoResponse extends Response{\n        public String name;\n        public boolean isSuccess;\n\n        public DemoResponse(){\n\n        }\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/test/resources/application.properties",
    "content": ""
  },
  {
    "path": "cola-components/cola-component-catchlog-starter/src/test/resources/logback-test.xml",
    "content": "<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <!--定义了两个appender，日志会通过往这两个appender里面写-->\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n\n    <!--应用日志-->\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n    <logger name=\"com.alibaba.cola.catchlog\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-components/cola-component-domain-starter/README.md",
    "content": "## 作用\n主要提供了@Entity注解，该注解将Spring的Bean的scope定义为prototype，因为Domain Entity是有状态的，不能\n进行多线程共享，所以必须是多实例的。\n\n另外提供了DomainFactory辅助类，帮助应用创建Domain Entity。\n\n\n"
  },
  {
    "path": "cola-components/cola-component-domain-starter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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    <parent>\n        <groupId>com.alibaba.cola</groupId>\n        <artifactId>cola-components-parent</artifactId>\n        <version>5.x-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>cola-component-domain-starter</artifactId>\n    <packaging>jar</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-autoconfigure</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-configuration-processor</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <!-- 测试包 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-components/cola-component-domain-starter/src/main/java/com/alibaba/cola/domain/ApplicationContextHelper.java",
    "content": "package com.alibaba.cola.domain;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.stereotype.Component;\n\n/**\n * ApplicationContextHelper\n *\n * @author Frank Zhang\n * @date 2020-11-14 1:58 PM\n */\n@Component(\"colaDomainApplicationContextHelper\")\npublic class ApplicationContextHelper implements ApplicationContextAware {\n    private static ApplicationContext applicationContext;\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        ApplicationContextHelper.applicationContext = applicationContext;\n    }\n\n    public static <T> T getBean(Class<T> targetClz) {\n        T beanInstance = null;\n        //优先按type查\n        try {\n            beanInstance = (T)applicationContext.getBean(targetClz);\n        } catch (Exception e) {\n        }\n        //按name查\n        if (beanInstance == null) {\n            String simpleName = targetClz.getSimpleName();\n            //首字母小写\n            simpleName = Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);\n            beanInstance = (T)applicationContext.getBean(simpleName);\n        }\n        if (beanInstance == null) {\n            throw new RuntimeException(\"Component \" + targetClz + \" can not be found in Spring Container\");\n        }\n        return beanInstance;\n    }\n\n    public static Object getBean(String claz) {\n        return ApplicationContextHelper.applicationContext.getBean(claz);\n    }\n\n    public static <T> T getBean(String name, Class<T> requiredType) {\n        return ApplicationContextHelper.applicationContext.getBean(name, requiredType);\n    }\n\n    public static <T> T getBean(Class<T> requiredType, Object... params) {\n        return ApplicationContextHelper.applicationContext.getBean(requiredType, params);\n    }\n\n    public static ApplicationContext getApplicationContext() {\n        return applicationContext;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-domain-starter/src/main/java/com/alibaba/cola/domain/DomainAutoConfiguration.java",
    "content": "package com.alibaba.cola.domain;\n\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n/**\n * @ Description   :\n * @ Author        :  Frank Zhang\n * @ CreateDate    :  2020/11/09\n * @ Version       :  1.0\n */\n@Configuration\npublic class DomainAutoConfiguration {\n\n    @Bean\n    @ConditionalOnMissingBean(ApplicationContextHelper.class)\n    public ApplicationContextHelper applicationContextHelper() {\n        return new ApplicationContextHelper();\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-domain-starter/src/main/java/com/alibaba/cola/domain/DomainFactory.java",
    "content": "package com.alibaba.cola.domain;\n\n/**\n * DomainFactory\n *\n * @author Frank Zhang\n * @date 2019-01-03 2:41 PM\n */\npublic class DomainFactory {\n\n    public static <T> T create(Class<T> entityClz){\n        return ApplicationContextHelper.getBean(entityClz);\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-domain-starter/src/main/java/com/alibaba/cola/domain/Entity.java",
    "content": "package com.alibaba.cola.domain;\n\nimport org.springframework.beans.factory.config.ConfigurableBeanFactory;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\nimport java.lang.annotation.*;\n\n/**\n * Entity, Entity Object is prototype and is not thread-safe\n *\n * @author Frank Zhang\n * @date 2019-01-03 2:53 PM\n */\n@Inherited\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE})\n@Component\n@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)\npublic @interface Entity {\n}\n"
  },
  {
    "path": "cola-components/cola-component-domain-starter/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.boot.autoconfigure.EnableAutoConfiguration = com.alibaba.cola.domain.DomainAutoConfiguration\n"
  },
  {
    "path": "cola-components/cola-component-domain-starter/src/test/java/com/alibaba/cola/domain/Application.java",
    "content": "package com.alibaba.cola.domain;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * Application\n *\n * @author Frank Zhang\n * @date 2020-11-10 3:58 PM\n */\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n\n        Customer customer = DomainFactory.create(Customer.class);\n\n        System.out.println(\"Customer purchase power score : \" + customer.getPurchasePowerScore());\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-domain-starter/src/test/java/com/alibaba/cola/domain/Customer.java",
    "content": "package com.alibaba.cola.domain;\n\nimport com.alibaba.cola.domain.Entity;\nimport jakarta.annotation.Resource;\n\n\n/**\n * Customer\n *\n * @author Frank Zhang\n * @date 2020-11-14 2:43 PM\n */\n@Entity\npublic class Customer {\n    private String name;\n\n    private Integer age;\n\n    @Resource\n    private PurchasePowerGateway purchasePowerGateway;\n\n    public Integer getAge() {\n        return age;\n    }\n\n    public void setAge(Integer age) {\n        this.age = age;\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 Long getPurchasePowerScore(){\n        return purchasePowerGateway.getScore();\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-domain-starter/src/test/java/com/alibaba/cola/domain/PurchasePowerGateway.java",
    "content": "package com.alibaba.cola.domain;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * PurchasePowerGateway\n *\n * @author Frank Zhang\n * @date 2020-11-14 2:45 PM\n */\n@Component\npublic class PurchasePowerGateway {\n\n    public Long getScore(){\n        return 96L;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-domain-starter/src/test/resources/application.properties",
    "content": ""
  },
  {
    "path": "cola-components/cola-component-domain-starter/src/test/resources/logback-test.xml",
    "content": "<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <!--定义了两个appender，日志会通过往这两个appender里面写-->\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n\n    <!--应用日志-->\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n    <logger name=\"com.alibaba.cola.domain\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-components/cola-component-dto/.gitignore",
    "content": "target/\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-components/cola-component-dto/README.md",
    "content": "## 作用\n制定了DTO的相关规范，一是，为了复用；二是，使得应用层面的Logging和异常处理AOP成为可能。\n\n关于Logging和异常处理的更多信息，请参考cola-component-catchlog-starter。\n\n\n\n"
  },
  {
    "path": "cola-components/cola-component-dto/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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    <parent>\n        <groupId>com.alibaba.cola</groupId>\n        <artifactId>cola-components-parent</artifactId>\n        <version>5.x-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>cola-component-dto</artifactId>\n    <packaging>jar</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n</project>\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/main/java/com/alibaba/cola/dto/ClientObject.java",
    "content": "package com.alibaba.cola.dto;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * This is the object communicate with Client.\n * The clients could be view layer or other HSF Consumers\n * @author fulan.zjf 2017-10-27 PM 12:19:15\n */\npublic abstract class ClientObject implements Serializable{\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * This is for extended values\n     */\n    protected Map<String, Object> extValues = new HashMap<String, Object>();\n\n    public Object getExtField(String key){\n        if(extValues != null){\n            return extValues.get(key);\n        }\n        return null;\n    }\n\n    public void putExtField(String fieldName, Object value){\n        this.extValues.put(fieldName, value);\n    }\n\n    public Map<String, Object> getExtValues() {\n        return extValues;\n    }\n\n    public void setExtValues(Map<String, Object> extValues) {\n        this.extValues = extValues;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/main/java/com/alibaba/cola/dto/Command.java",
    "content": "package com.alibaba.cola.dto;\n\n/**\n * Command request from Client.\n *\n * @author Frank Zhang 2020.11.13\n *\n */\npublic abstract class Command extends DTO {\n\n    private static final long serialVersionUID = 1L;\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/main/java/com/alibaba/cola/dto/DTO.java",
    "content": "package com.alibaba.cola.dto;\n\nimport java.io.Serializable;\n\n/**\n * Data Transfer object, including Command, Query and Response,\n *\n * Command and Query is CQRS concept.\n *\n * @author Frank Zhang 2020.11.13\n *\n */\npublic abstract class DTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/main/java/com/alibaba/cola/dto/MultiResponse.java",
    "content": "package com.alibaba.cola.dto;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Response with batch record to return,\n * usually use in conditional query\n * <p/>\n * Created by Danny.Lee on 2017/11/1.\n */\npublic class MultiResponse<T> extends Response {\n\n    private static final long serialVersionUID = 1L;\n\n    private Collection<T> data;\n\n    public List<T> getData() {\n        if (null == data) {\n            return Collections.emptyList();\n        }\n        if (data instanceof List) {\n            return (List<T>) data;\n        }\n        return new ArrayList<>(data);\n    }\n\n    public void setData(Collection<T> data) {\n        this.data = data;\n    }\n\n    public boolean isEmpty() {\n        return data == null || data.isEmpty();\n    }\n\n    public boolean isNotEmpty() {\n        return !isEmpty();\n    }\n\n    public static MultiResponse buildSuccess() {\n        MultiResponse response = new MultiResponse();\n        response.setSuccess(true);\n        return response;\n    }\n\n    public static MultiResponse buildFailure(String errCode, String errMessage) {\n        MultiResponse response = new MultiResponse();\n        response.setSuccess(false);\n        response.setErrCode(errCode);\n        response.setErrMessage(errMessage);\n        return response;\n    }\n\n    public static <T> MultiResponse<T> of(Collection<T> data) {\n        MultiResponse<T> response = new MultiResponse<>();\n        response.setSuccess(true);\n        response.setData(data);\n        return response;\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/main/java/com/alibaba/cola/dto/PageQuery.java",
    "content": "package com.alibaba.cola.dto;\n\n/**\n * Page Query Param\n *\n * @author jacky\n */\npublic abstract class PageQuery extends Query {\n    private static final long serialVersionUID = 1L;\n\n    public static final String ASC = \"ASC\";\n\n    public static final String DESC = \"DESC\";\n\n    private static final int DEFAULT_PAGE_SIZE = 10;\n\n    private int pageSize = DEFAULT_PAGE_SIZE;\n\n    private int pageIndex = 1;\n\n    private String orderBy;\n\n    private String orderDirection = DESC;\n\n    private String groupBy;\n\n    private boolean needTotalCount = true;\n\n    public int getPageIndex() {\n        if (pageIndex < 1) {\n            return 1;\n        }\n        return pageIndex;\n    }\n\n    public PageQuery setPageIndex(int pageIndex) {\n        this.pageIndex = pageIndex;\n        return this;\n    }\n\n    public int getPageSize() {\n        if (pageSize < 1) {\n            pageSize = DEFAULT_PAGE_SIZE;\n        }\n        return pageSize;\n    }\n\n    public PageQuery setPageSize(int pageSize) {\n        if (pageSize < 1) {\n            pageSize = DEFAULT_PAGE_SIZE;\n        }\n        this.pageSize = pageSize;\n        return this;\n    }\n\n    public int getOffset() {\n        return (getPageIndex() - 1) * getPageSize();\n    }\n\n    public String getOrderBy() {\n        return orderBy;\n    }\n\n    public PageQuery setOrderBy(String orderBy) {\n        this.orderBy = orderBy;\n        return this;\n    }\n\n    public String getOrderDirection() {\n        return orderDirection;\n    }\n\n    public PageQuery setOrderDirection(String orderDirection) {\n        if (ASC.equalsIgnoreCase(orderDirection) || DESC.equalsIgnoreCase(orderDirection)) {\n            this.orderDirection = orderDirection;\n        }\n        return this;\n    }\n\n    public String getGroupBy() {\n        return groupBy;\n    }\n\n    public void setGroupBy(String groupBy) {\n        this.groupBy = groupBy;\n    }\n\n    public boolean isNeedTotalCount() {\n        return needTotalCount;\n    }\n\n    public void setNeedTotalCount(boolean needTotalCount) {\n        this.needTotalCount = needTotalCount;\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/main/java/com/alibaba/cola/dto/PageResponse.java",
    "content": "package com.alibaba.cola.dto;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Response with batch page record to return,\n * usually use in page query\n * <p/>\n * Created by xiaochu.lbj on 2020/06/30.\n */\npublic class PageResponse<T> extends Response {\n\n    private static final long serialVersionUID = 1L;\n\n    private int totalCount = 0;\n\n    private int pageSize = 1;\n\n    private int pageIndex = 1;\n\n    private Collection<T> data;\n\n    public int getTotalCount() {\n        return totalCount;\n    }\n\n    public void setTotalCount(int totalCount) {\n        this.totalCount = totalCount;\n    }\n\n    public int getPageSize() {\n        if (pageSize < 1) {\n            return 1;\n        }\n        return pageSize;\n    }\n\n    public void setPageSize(int pageSize) {\n        if (pageSize < 1) {\n            this.pageSize = 1;\n        } else {\n            this.pageSize = pageSize;\n        }\n    }\n\n    public int getPageIndex() {\n        if (pageIndex < 1) {\n            return 1;\n        }\n        return pageIndex;\n    }\n\n    public void setPageIndex(int pageIndex) {\n        if (pageIndex < 1) {\n            this.pageIndex = 1;\n        } else {\n            this.pageIndex = pageIndex;\n        }\n    }\n\n    public List<T> getData() {\n        if (null == data) {\n            return Collections.emptyList();\n        }\n        if (data instanceof List) {\n            return (List<T>) data;\n        }\n        return new ArrayList<>(data);\n    }\n\n    public void setData(Collection<T> data) {\n        this.data = data;\n    }\n\n    public int getTotalPages() {\n        return this.totalCount % this.pageSize == 0 ? this.totalCount\n            / this.pageSize : (this.totalCount / this.pageSize) + 1;\n    }\n\n    public boolean isEmpty() {\n        return data == null || data.isEmpty();\n    }\n\n    public boolean isNotEmpty() {\n        return !isEmpty();\n    }\n\n    public static PageResponse buildSuccess() {\n        PageResponse response = new PageResponse();\n        response.setSuccess(true);\n        return response;\n    }\n\n    public static PageResponse buildFailure(String errCode, String errMessage) {\n        PageResponse response = new PageResponse();\n        response.setSuccess(false);\n        response.setErrCode(errCode);\n        response.setErrMessage(errMessage);\n        return response;\n    }\n\n    public static <T> PageResponse<T> of(int pageSize, int pageIndex) {\n        PageResponse<T> response = new PageResponse<>();\n        response.setSuccess(true);\n        response.setData(Collections.emptyList());\n        response.setTotalCount(0);\n        response.setPageSize(pageSize);\n        response.setPageIndex(pageIndex);\n        return response;\n    }\n\n    public static <T> PageResponse<T> of(Collection<T> data, int totalCount, int pageSize, int pageIndex) {\n        PageResponse<T> response = new PageResponse<>();\n        response.setSuccess(true);\n        response.setData(data);\n        response.setTotalCount(totalCount);\n        response.setPageSize(pageSize);\n        response.setPageIndex(pageIndex);\n        return response;\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/main/java/com/alibaba/cola/dto/Query.java",
    "content": "package com.alibaba.cola.dto;\n\n/**\n * Query request from Client.\n *\n * @author Frank Zhang 2020.11.13\n *\n */\npublic abstract class Query extends Command {\n\n    private static final long serialVersionUID = 1L;\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/main/java/com/alibaba/cola/dto/Response.java",
    "content": "package com.alibaba.cola.dto;\n\n/**\n * Response to caller\n *\n * @author fulan.zjf 2017年10月21日 下午8:53:17\n */\npublic class Response extends DTO {\n\n    private static final long serialVersionUID = 1L;\n\n    private boolean success;\n\n    private String errCode;\n\n    private String errMessage;\n\n    public boolean isSuccess() {\n        return success;\n    }\n\n    public void setSuccess(boolean success) {\n        this.success = success;\n    }\n\n    public String getErrCode() {\n        return errCode;\n    }\n\n    public void setErrCode(String errCode) {\n        this.errCode = errCode;\n    }\n\n    public String getErrMessage() {\n        return errMessage;\n    }\n\n    public void setErrMessage(String errMessage) {\n        this.errMessage = errMessage;\n    }\n\n    @Override\n    public String toString() {\n        return \"Response [success=\" + success + \", errCode=\" + errCode + \", errMessage=\" + errMessage + \"]\";\n    }\n\n    public static Response buildSuccess() {\n        Response response = new Response();\n        response.setSuccess(true);\n        return response;\n    }\n\n    public static Response buildFailure(String errCode, String errMessage) {\n        Response response = new Response();\n        response.setSuccess(false);\n        response.setErrCode(errCode);\n        response.setErrMessage(errMessage);\n        return response;\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/main/java/com/alibaba/cola/dto/Scope.java",
    "content": "package com.alibaba.cola.dto;\n\n/**\n * 结果范围控制\n *\n * @author xiaochu.lbj\n */\npublic abstract class Scope extends DTO {\n\n    private static final long serialVersionUID = 1L;\n}\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/main/java/com/alibaba/cola/dto/SingleResponse.java",
    "content": "package com.alibaba.cola.dto;\n\n/**\n * Response with single record to return\n * <p/>\n * Created by Danny.Lee on 2017/11/1.\n */\npublic class SingleResponse<T> extends Response {\n\n    private static final long serialVersionUID = 1L;\n\n    private T data;\n\n    public T getData() {\n        return data;\n    }\n\n    public void setData(T data) {\n        this.data = data;\n    }\n\n    public static SingleResponse buildSuccess() {\n        SingleResponse response = new SingleResponse();\n        response.setSuccess(true);\n        return response;\n    }\n\n    public static SingleResponse buildFailure(String errCode, String errMessage) {\n        SingleResponse response = new SingleResponse();\n        response.setSuccess(false);\n        response.setErrCode(errCode);\n        response.setErrMessage(errMessage);\n        return response;\n    }\n\n    public static <T> SingleResponse<T> of(T data) {\n        SingleResponse<T> response = new SingleResponse<>();\n        response.setSuccess(true);\n        response.setData(data);\n        return response;\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/main/java/com/alibaba/cola/extension/BizScenario.java",
    "content": "package com.alibaba.cola.extension;\n\n\n/**\n * BizScenario（业务场景）= bizId + useCase + scenario, which can uniquely identify a user scenario.\n *\n * @author Frank Zhang\n * @date 2019-08-20 12:07\n */\npublic class BizScenario {\n    public final static String DEFAULT_BIZ_ID = \"#defaultBizId#\";\n    public final static String DEFAULT_USE_CASE = \"#defaultUseCase#\";\n    public final static String DEFAULT_SCENARIO = \"#defaultScenario#\";\n    private final static String DOT_SEPARATOR = \".\";\n\n    /**\n     * bizId is used to identify a business, such as \"tmall\", it's nullable if there is only one biz\n     */\n    private String bizId = DEFAULT_BIZ_ID;\n\n    /**\n     * useCase is used to identify a use case, such as \"placeOrder\", can not be null\n     */\n    private String useCase = DEFAULT_USE_CASE;\n\n    /**\n     * scenario is used to identify a use case, such as \"88vip\",\"normal\", can not be null\n     */\n    private String scenario = DEFAULT_SCENARIO;\n\n    /**\n     * For above case, the BizScenario will be \"tmall.placeOrder.88vip\",\n     * with this code, we can provide extension processing other than \"tmall.placeOrder.normal\" scenario.\n     *\n     * @return\n     */\n    public String getUniqueIdentity(){\n        return bizId + DOT_SEPARATOR + useCase + DOT_SEPARATOR + scenario;\n    }\n\n    public static BizScenario valueOf(String bizId, String useCase, String scenario){\n        BizScenario bizScenario = new BizScenario();\n        bizScenario.bizId = bizId;\n        bizScenario.useCase = useCase;\n        bizScenario.scenario = scenario;\n        return bizScenario;\n    }\n\n    public static BizScenario valueOf(String bizId, String useCase){\n        return BizScenario.valueOf(bizId, useCase, DEFAULT_SCENARIO);\n    }\n\n    public static BizScenario valueOf(String bizId){\n        return BizScenario.valueOf(bizId, DEFAULT_USE_CASE, DEFAULT_SCENARIO);\n    }\n\n    public static BizScenario newDefault(){\n        return BizScenario.valueOf(DEFAULT_BIZ_ID, DEFAULT_USE_CASE, DEFAULT_SCENARIO);\n    }\n\n    public String getIdentityWithDefaultScenario(){\n        return bizId + DOT_SEPARATOR + useCase + DOT_SEPARATOR + DEFAULT_SCENARIO;\n    }\n\n    public String getIdentityWithDefaultUseCase(){\n        return bizId + DOT_SEPARATOR + DEFAULT_USE_CASE + DOT_SEPARATOR + DEFAULT_SCENARIO;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/test/java/com/alibaba/cola/Test.java",
    "content": "package com.alibaba.cola;\n\npublic class Test {\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-dto/src/test/resources/logback-test.xml",
    "content": "<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n       <encoder>\n           <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>\n           <charset>utf8</charset>\n       </encoder>\n   </appender>\n\n   <!--rootLogger是默认的logger-->\n   <root level=\"INFO\">\n       <!--定义了两个appender，日志会通过往这两个appender里面写-->\n       <appender-ref ref=\"CONSOLE\"/>\n   </root>\n\n   <!--应用日志-->\n   <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n   <logger name=\"com.alibaba.cola\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-components/cola-component-exception/.gitignore",
    "content": "target/\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-components/cola-component-exception/README.md",
    "content": "## 作用\n制定了Exception的相关规范，一是，为了复用；二是，使得应用层面的Logging和异常处理AOP成为可能。\n\n实际上，对于应用系统而言，只有三种类型的异常：\n1. BizException：业务异常，有明确的业务语义，不需要记录Error日志，不需要Retry\n2. SysException：已知的系统异常，需要记录Error日志，可以Retry\n3. Exception：未知的其它异常，需要完整的Error Stack日志，可以Retry\n\n\n"
  },
  {
    "path": "cola-components/cola-component-exception/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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    <parent>\n        <groupId>com.alibaba.cola</groupId>\n        <artifactId>cola-components-parent</artifactId>\n        <version>5.x-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>cola-component-exception</artifactId>\n    <packaging>jar</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n</project>\n"
  },
  {
    "path": "cola-components/cola-component-exception/src/main/java/com/alibaba/cola/exception/Assert.java",
    "content": "package com.alibaba.cola.exception;\n\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * Assertion utility class that assists in validating arguments.\n *\n * <p>Useful for identifying programmer errors early and clearly at runtime.\n *\n * <p>For example, if the contract of a public method states it does not\n * allow {@code null} arguments, {@code Assert} can be used to validate that\n * contract.\n *\n * For example:\n *\n * <pre class=\"code\">\n * Assert.notNull(clazz, \"The class must not be null\");\n * Assert.isTrue(i > 0, \"The value must be greater than zero\");</pre>\n *\n * This class is empowered by  {@link org.springframework.util.Assert}\n *\n * @author Frank Zhang\n * @date 2019-01-13 11:49 AM\n */\npublic abstract class Assert {\n\n    /**\n     * Assert a boolean expression, throwing {@code BizException}\n     *\n     * for example\n     *\n     * <pre class=\"code\">Assert.isTrue(i != 0, errorCode.B_ORDER_illegalNumber, \"The order number can not be zero\");</pre>\n     *\n     * @param expression a boolean expression\n     * @param errorCode\n     * @param errMessage the exception message to use if the assertion fails\n     * @throws BizException if expression is {@code false}\n     */\n    public static void isTrue(boolean expression, String errorCode, String errMessage) {\n        if (!expression) {\n            throw new BizException(errorCode, errMessage);\n        }\n    }\n\n    /**\n     * Assert a boolean expression, if expression is true, throwing {@code BizException}\n     *\n     * for example\n     *\n     * <pre class=\"code\">Assert.isFalse(i == 0, errorCode.B_ORDER_illegalNumber, \"The order number can not be zero\");</pre>\n     *\n     * This is more intuitive than isTure.\n     */\n    public static void isFalse(boolean expression, String errorCode, String errMessage) {\n        if (expression) {\n            throw new BizException(errorCode, errMessage);\n        }\n    }\n\n    public static void isTrue(boolean expression, String errMessage) {\n        if (!expression) {\n            throw new BizException(errMessage);\n        }\n    }\n\n    public static void isFalse(boolean expression, String errMessage) {\n        if (expression) {\n            throw new BizException(errMessage);\n        }\n    }\n\n    public static void isTrue(boolean expression) {\n        isTrue(expression, \"[Assertion failed] Must be true\");\n    }\n\n    public static void isFalse(boolean expression) {\n        isFalse(expression, \"[Assertion failed] Must be false\");\n    }\n\n    public static void notNull(Object object, String errorCode, String errMessage) {\n        if (object == null) {\n            throw new BizException(errorCode, errMessage);\n        }\n    }\n\n    public static void notNull(Object object, String errMessage) {\n        if (object == null) {\n            throw new BizException(errMessage);\n        }\n    }\n\n    public static void notNull(Object object) {\n        notNull(object, \"[Assertion failed] Must not null\");\n    }\n\n    public static void notEmpty(Collection<?> collection, String errorCode, String errMessage) {\n        if (collection == null || collection.isEmpty()) {\n            throw new BizException(errorCode, errMessage);\n        }\n    }\n\n    public static void notEmpty(Collection<?> collection, String errMessage) {\n        if (collection == null || collection.isEmpty()) {\n            throw new BizException(errMessage);\n        }\n    }\n\n    public static void notEmpty(Collection<?> collection) {\n        notEmpty(collection, \"[Assertion failed] Collection must not be empty: it must contain at least 1 element\");\n    }\n\n    public static void notEmpty(Map<?, ?> map, String errorCode, String errMessage) {\n        if (map == null || map.isEmpty()) {\n            throw new BizException(errorCode, errMessage);\n        }\n    }\n\n    public static void notEmpty(Map<?, ?> map, String errMessage) {\n        if (map == null || map.isEmpty()) {\n            throw new BizException(errMessage);\n        }\n    }\n\n    public static void notEmpty(Map<?, ?> map) {\n        notEmpty(map, \"[Assertion failed] Map must not be empty: it must contain at least one entry\");\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-exception/src/main/java/com/alibaba/cola/exception/BaseException.java",
    "content": "package com.alibaba.cola.exception;\n\n/**\n * Base Exception is the parent of all exceptions\n *\n * @author fulan.zjf 2017年10月22日 上午12:00:39\n */\npublic abstract class BaseException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    private String errCode;\n\n    public BaseException(String errMessage) {\n        super(errMessage);\n    }\n\n    public BaseException(String errCode, String errMessage) {\n        super(errMessage);\n        this.errCode = errCode;\n    }\n\n    public BaseException(String errMessage, Throwable e) {\n        super(errMessage, e);\n    }\n\n    public BaseException(String errCode, String errMessage, Throwable e) {\n        super(errMessage, e);\n        this.errCode = errCode;\n    }\n\n    public String getErrCode() {\n        return errCode;\n    }\n\n    public void setErrCode(String errCode) {\n        this.errCode = errCode;\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-exception/src/main/java/com/alibaba/cola/exception/BizException.java",
    "content": "package com.alibaba.cola.exception;\n\n/**\n * BizException is known Exception, no need retry\n *\n * @author Frank Zhang\n */\npublic class BizException extends BaseException {\n\n    private static final long serialVersionUID = 1L;\n\n    private static final String DEFAULT_ERR_CODE = \"BIZ_ERROR\";\n\n    public BizException(String errMessage) {\n        super(DEFAULT_ERR_CODE, errMessage);\n    }\n\n    public BizException(String errCode, String errMessage) {\n        super(errCode, errMessage);\n    }\n\n    public BizException(String errMessage, Throwable e) {\n        super(DEFAULT_ERR_CODE, errMessage, e);\n    }\n\n    public BizException(String errorCode, String errMessage, Throwable e) {\n        super(errorCode, errMessage, e);\n    }\n\n}"
  },
  {
    "path": "cola-components/cola-component-exception/src/main/java/com/alibaba/cola/exception/ExceptionFactory.java",
    "content": "package com.alibaba.cola.exception;\n\n/**\n * @ Description   :  异常工厂实现\n * @ Author        :  da.xue\n * @ CreateDate    :  2020/04/11\n * @ Version       :  1.0\n */\npublic class ExceptionFactory {\n\n    public static BizException bizException(String errorMessage) {\n        return new BizException(errorMessage);\n    }\n\n    public static BizException bizException(String errorCode, String errorMessage) {\n        return new BizException(errorCode, errorMessage);\n    }\n\n    public static SysException sysException(String errorMessage) {\n        return new SysException(errorMessage);\n    }\n\n    public static SysException sysException(String errorCode, String errorMessage) {\n        return new SysException(errorCode, errorMessage);\n    }\n\n    public static SysException sysException(String errorMessage, Throwable e) {\n        return new SysException(errorMessage, e);\n    }\n\n    public static SysException sysException(String errorCode, String errorMessage, Throwable e) {\n        return new SysException(errorCode, errorMessage, e);\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-exception/src/main/java/com/alibaba/cola/exception/SysException.java",
    "content": "package com.alibaba.cola.exception;\n\n/**\n * System Exception is unexpected Exception, retry might work again\n *\n * @author Danny.Lee 2018/1/27\n */\npublic class SysException extends BaseException {\n\n    private static final long serialVersionUID = 1L;\n\n    private static final String DEFAULT_ERR_CODE = \"SYS_ERROR\";\n\n    public SysException(String errMessage) {\n        super(DEFAULT_ERR_CODE, errMessage);\n    }\n\n    public SysException(String errCode, String errMessage) {\n        super(errCode, errMessage);\n    }\n\n    public SysException(String errMessage, Throwable e) {\n        super(DEFAULT_ERR_CODE, errMessage, e);\n    }\n\n    public SysException(String errorCode, String errMessage, Throwable e) {\n        super(errorCode, errMessage, e);\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-exception/src/test/java/com/alibaba/cola/exception/Test.java",
    "content": "package com.alibaba.cola.exception;\n\npublic class Test {\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-exception/src/test/resources/logback-test.xml",
    "content": "<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n       <encoder>\n           <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>\n           <charset>utf8</charset>\n       </encoder>\n   </appender>\n\n   <!--rootLogger是默认的logger-->\n   <root level=\"INFO\">\n       <!--定义了两个appender，日志会通过往这两个appender里面写-->\n       <appender-ref ref=\"CONSOLE\"/>\n   </root>\n\n   <!--应用日志-->\n   <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n   <logger name=\"com.alibaba.cola.exception\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/README.md",
    "content": "## 作用\n该组件继承了老Cola Framework中的扩展点功能，旨在通过统一的扩展形式来支撑业务的变化。\n\n## 原理\nhttps://blog.csdn.net/significantfrank/article/details/100074716\n\n## 使用介绍\n参看测试代码`com.alibaba.cola.extension.ExtensionTest`\n\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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    <parent>\n        <groupId>com.alibaba.cola</groupId>\n        <artifactId>cola-components-parent</artifactId>\n        <version>5.x-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>cola-component-extension-starter</artifactId>\n    <packaging>jar</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-autoconfigure</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-configuration-processor</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- 工具包 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- 日志包 -->\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- cola组件依赖 -->\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-dto</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-exception</artifactId>\n            <version>${project.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-domain-starter</artifactId>\n            <version>${project.version}</version>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- 测试包 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/Extension.java",
    "content": "/*\n * Copyright 2017 Alibaba.com All right reserved. This software is the\n * confidential and proprietary information of Alibaba.com (\"Confidential\n * Information\"). You shall not disclose such Confidential Information and shall\n * use it only in accordance with the terms of the license agreement you entered\n * into with Alibaba.com.\n */\npackage com.alibaba.cola.extension;\n\nimport org.springframework.stereotype.Component;\n\nimport java.lang.annotation.*;\n\n/**\n * Extension\n * @author fulan.zjf 2017-11-05\n */\n@Inherited\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE})\n@Component\npublic @interface Extension {\n    String bizId()  default BizScenario.DEFAULT_BIZ_ID;\n    String useCase() default BizScenario.DEFAULT_USE_CASE;\n    String scenario() default BizScenario.DEFAULT_SCENARIO;\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/ExtensionAutoConfiguration.java",
    "content": "package com.alibaba.cola.extension;\n\nimport com.alibaba.cola.extension.register.ExtensionBootstrap;\nimport com.alibaba.cola.extension.register.ExtensionRegister;\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;\n\n/**\n * @ Description   :\n * @ Author        :  Frank Zhang\n * @ CreateDate    :  2020/11/09\n * @ Version       :  1.0\n */\n@Configuration\npublic class ExtensionAutoConfiguration {\n\n    @Bean(initMethod = \"init\")\n    @ConditionalOnMissingBean(ExtensionBootstrap.class)\n    public ExtensionBootstrap bootstrap() {\n        return new ExtensionBootstrap();\n    }\n\n    @Bean\n    @ConditionalOnMissingBean(ExtensionRepository.class)\n    public ExtensionRepository repository() {\n        return new ExtensionRepository();\n    }\n\n    @Bean\n    @ConditionalOnMissingBean(ExtensionExecutor.class)\n    public ExtensionExecutor executor() {\n        return new ExtensionExecutor();\n    }\n\n    @Bean\n    @ConditionalOnMissingBean(ExtensionRegister.class)\n    public ExtensionRegister register() {\n        return new ExtensionRegister();\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/ExtensionCoordinate.java",
    "content": "/*\n * Copyright 2017 Alibaba.com All right reserved. This software is the\n * confidential and proprietary information of Alibaba.com (\"Confidential\n * Information\"). You shall not disclose such Confidential Information and shall\n * use it only in accordance with the terms of the license agreement you entered\n * into with Alibaba.com.\n */\npackage com.alibaba.cola.extension;\n\n/**\n * Extension Coordinate(扩展坐标) is used to uniquely position an Extension\n * @author fulan.zjf 2017-11-05\n */\npublic class ExtensionCoordinate {\n\n    private final String extensionPointName;\n    private final String bizScenarioUniqueIdentity;\n\n    /**\n     * Wrapper\n     */\n    private Class<?> extensionPointClass;\n    private BizScenario bizScenario;\n\n    public Class getExtensionPointClass() {\n        return extensionPointClass;\n    }\n\n    public BizScenario getBizScenario() {\n        return bizScenario;\n    }\n\n    public static ExtensionCoordinate valueOf(Class<?> extPtClass, BizScenario bizScenario){\n        return new ExtensionCoordinate(extPtClass, bizScenario);\n    }\n\n    public ExtensionCoordinate(Class<?> extPtClass, BizScenario bizScenario){\n        this.extensionPointClass = extPtClass;\n        this.extensionPointName = extPtClass.getName();\n        this.bizScenario = bizScenario;\n        this.bizScenarioUniqueIdentity = bizScenario.getUniqueIdentity();\n    }\n\n    public ExtensionCoordinate(String extensionPoint, String bizScenario){\n        this.extensionPointName = extensionPoint;\n        this.bizScenarioUniqueIdentity = bizScenario;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((bizScenarioUniqueIdentity == null) ? 0 : bizScenarioUniqueIdentity.hashCode());\n        result = prime * result + ((extensionPointName == null) ? 0 : extensionPointName.hashCode());\n        return result;\n    }\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n          return true;\n        }\n\n        if (obj == null) {\n          return false;\n        }\n\n        if (getClass() != obj.getClass()) {\n          return false;\n        }\n\n        ExtensionCoordinate other = (ExtensionCoordinate) obj;\n        if (bizScenarioUniqueIdentity == null) {\n            if (other.bizScenarioUniqueIdentity != null) {\n              return false;\n            }\n        } else if (!bizScenarioUniqueIdentity.equals(other.bizScenarioUniqueIdentity)) {\n          return false;\n        }\n        if (extensionPointName == null) {\n          return other.extensionPointName == null;\n        } else {\n          return extensionPointName.equals(other.extensionPointName);\n        }\n    }\n\n    @Override\n    public String toString() {\n        return \"ExtensionCoordinate [extensionPointName=\" + extensionPointName + \", bizScenarioUniqueIdentity=\" + bizScenarioUniqueIdentity + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/ExtensionException.java",
    "content": "package com.alibaba.cola.extension;\n\n/**\n * 扩展点初始化或者查找失败时，使用次异常\n * <p>\n * 扩展点初始化或者查找失败时，使用次异常\n * <p>\n *\n * @author ***flying@126.com\n * @version 1.0.0\n * @since 1.0.0 2022/9/26\n */\npublic class ExtensionException extends RuntimeException {\n\n    private String errCode;\n\n    public ExtensionException(String errMessage) {\n        super(errMessage);\n    }\n\n    public ExtensionException(String errCode, String errMessage) {\n        super(errMessage);\n        this.errCode = errCode;\n    }\n\n    public ExtensionException(String errMessage, Throwable e) {\n        super(errMessage, e);\n    }\n\n    public ExtensionException(String errCode, String errMessage, Throwable e) {\n        super(errMessage, e);\n        this.errCode = errCode;\n    }\n\n    public String getErrCode() {\n        return errCode;\n    }\n\n    public void setErrCode(String errCode) {\n        this.errCode = errCode;\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/ExtensionExecutor.java",
    "content": "/*\n * Copyright 2017 Alibaba.com All right reserved. This software is the\n * confidential and proprietary information of Alibaba.com (\"Confidential\n * Information\"). You shall not disclose such Confidential Information and shall\n * use it only in accordance with the terms of the license agreement you entered\n * into with Alibaba.com.\n */\npackage com.alibaba.cola.extension;\n\nimport com.alibaba.cola.extension.register.AbstractComponentExecutor;\nimport jakarta.annotation.Resource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n\n/**\n * ExtensionExecutor\n *\n * @author fulan.zjf 2017-11-05\n */\n@Component\npublic class ExtensionExecutor extends AbstractComponentExecutor {\n\n    private static final String EXTENSION_NOT_FOUND = \"extension_not_found\";\n\n    private Logger logger = LoggerFactory.getLogger(ExtensionExecutor.class);\n\n    @Resource\n    private ExtensionRepository extensionRepository;\n\n    @Override\n    protected <C> C locateComponent(Class<C> targetClz, BizScenario bizScenario) {\n        C extension = locateExtension(targetClz, bizScenario);\n        logger.debug(\"[Located Extension]: \" + extension.getClass().getSimpleName());\n        return extension;\n    }\n\n    /**\n     * if the bizScenarioUniqueIdentity is \"ali.tmall.supermarket\"\n     * <p>\n     * the search path is as below:\n     * 1、first try to get extension by \"ali.tmall.supermarket\", if get, return it.\n     * 2、loop try to get extension by \"ali.tmall\", if get, return it.\n     * 3、loop try to get extension by \"ali\", if get, return it.\n     * 4、if not found, try the default extension\n     *\n     * @param targetClz\n     */\n    protected <Ext> Ext locateExtension(Class<Ext> targetClz, BizScenario bizScenario) {\n        checkNull(bizScenario);\n\n        Ext extension;\n\n        logger.debug(\"BizScenario in locateExtension is : \" + bizScenario.getUniqueIdentity());\n\n        // first try with full namespace\n        extension = firstTry(targetClz, bizScenario);\n        if (extension != null) {\n            return extension;\n        }\n\n        // second try with default scenario\n        extension = secondTry(targetClz, bizScenario);\n        if (extension != null) {\n            return extension;\n        }\n\n        // third try with default use case + default scenario\n        extension = defaultUseCaseTry(targetClz, bizScenario);\n        if (extension != null) {\n            return extension;\n        }\n\n        String errMessage = \"Can not find extension with ExtensionPoint: \" +\n                targetClz + \" BizScenario:\" + bizScenario.getUniqueIdentity();\n        throw new ExtensionException(EXTENSION_NOT_FOUND, errMessage);\n    }\n\n    /**\n     * first try with full namespace\n     * <p>\n     * example:  biz1.useCase1.scenario1\n     */\n    private <Ext> Ext firstTry(Class<Ext> targetClz, BizScenario bizScenario) {\n        logger.debug(\"First trying with \" + bizScenario.getUniqueIdentity());\n        return locate(targetClz.getName(), bizScenario.getUniqueIdentity());\n    }\n\n    /**\n     * second try with default scenario\n     * <p>\n     * example:  biz1.useCase1.#defaultScenario#\n     */\n    private <Ext> Ext secondTry(Class<Ext> targetClz, BizScenario bizScenario) {\n        logger.debug(\"Second trying with \" + bizScenario.getIdentityWithDefaultScenario());\n        return locate(targetClz.getName(), bizScenario.getIdentityWithDefaultScenario());\n    }\n\n    /**\n     * third try with default use case + default scenario\n     * <p>\n     * example:  biz1.#defaultUseCase#.#defaultScenario#\n     */\n    private <Ext> Ext defaultUseCaseTry(Class<Ext> targetClz, BizScenario bizScenario) {\n        logger.debug(\"Third trying with \" + bizScenario.getIdentityWithDefaultUseCase());\n        return locate(targetClz.getName(), bizScenario.getIdentityWithDefaultUseCase());\n    }\n\n    private <Ext> Ext locate(String name, String uniqueIdentity) {\n        final Ext ext = (Ext) extensionRepository.getExtensionRepo().\n                get(new ExtensionCoordinate(name, uniqueIdentity));\n        return ext;\n    }\n\n    private void checkNull(BizScenario bizScenario) {\n        if (bizScenario == null) {\n            throw new IllegalArgumentException(\"BizScenario can not be null for extension\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/ExtensionPointI.java",
    "content": "package com.alibaba.cola.extension;\n\n/**\n *  ExtensionPointI is the parent interface of all ExtensionPoints\n *  扩展点表示一块逻辑在不同的业务有不同的实现，使用扩展点做接口申明，然后用Extension（扩展）去实现扩展点。 \n * @author fulan.zjf 2017-10-22\n */\npublic interface ExtensionPointI {\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/ExtensionRepository.java",
    "content": "/*\n * Copyright 2017 Alibaba.com All right reserved. This software is the\n * confidential and proprietary information of Alibaba.com (\"Confidential\n * Information\"). You shall not disclose such Confidential Information and shall\n * use it only in accordance with the terms of the license agreement you entered\n * into with Alibaba.com.\n */\npackage com.alibaba.cola.extension;\n\nimport org.springframework.stereotype.Component;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * ExtensionRepository \n * @author fulan.zjf 2017-11-05\n */\n@Component\npublic class ExtensionRepository {\n\n    public Map<ExtensionCoordinate, ExtensionPointI> getExtensionRepo() {\n        return extensionRepo;\n    }\n\n    private Map<ExtensionCoordinate, ExtensionPointI> extensionRepo = new HashMap<>();\n\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/Extensions.java",
    "content": "package com.alibaba.cola.extension;\n\nimport org.springframework.stereotype.Component;\n\nimport java.lang.annotation.*;\n\n/**\n * because {@link Extension} only supports single coordinates,\n * this annotation is a supplement to {@link Extension} and supports multiple coordinates\n *\n * @author wangguoqiang wrote on 2022/10/10 12:19\n * @version 1.0\n * @see Extension\n */\n@Inherited\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE})\n@Component\npublic @interface Extensions {\n\n    String[] bizId() default BizScenario.DEFAULT_BIZ_ID;\n\n    String[] useCase() default BizScenario.DEFAULT_USE_CASE;\n\n    String[] scenario() default BizScenario.DEFAULT_SCENARIO;\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/register/AbstractComponentExecutor.java",
    "content": "package com.alibaba.cola.extension.register;\n\nimport com.alibaba.cola.extension.BizScenario;\nimport com.alibaba.cola.extension.ExtensionCoordinate;\n\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\n/**\n * @author fulan.zjf\n * @date 2017/12/21\n */\npublic abstract class AbstractComponentExecutor {\n\n    /**\n     * Execute extension with Response\n     *\n     * @param targetClz\n     * @param bizScenario\n     * @param exeFunction\n     * @param <R> Response Type\n     * @param <T> Parameter Type\n     * @return\n     */\n    public <R, T> R execute(Class<T> targetClz, BizScenario bizScenario, Function<T, R> exeFunction) {\n        T component = locateComponent(targetClz, bizScenario);\n        return exeFunction.apply(component);\n    }\n\n    public <R, T> R execute(ExtensionCoordinate extensionCoordinate, Function<T, R> exeFunction){\n        return execute((Class<T>) extensionCoordinate.getExtensionPointClass(), extensionCoordinate.getBizScenario(), exeFunction);\n    }\n\n    /**\n     * Execute extension without Response\n     *\n     * @param targetClz\n     * @param context\n     * @param exeFunction\n     * @param <T> Parameter Type\n     */\n    public <T> void executeVoid(Class<T> targetClz, BizScenario context, Consumer<T> exeFunction) {\n        T component = locateComponent(targetClz, context);\n        exeFunction.accept(component);\n    }\n\n    public <T> void executeVoid(ExtensionCoordinate extensionCoordinate, Consumer<T> exeFunction){\n        executeVoid(extensionCoordinate.getExtensionPointClass(), extensionCoordinate.getBizScenario(), exeFunction);\n    }\n\n    protected abstract <C> C locateComponent(Class<C> targetClz, BizScenario context);\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/register/ExtensionBootstrap.java",
    "content": "package com.alibaba.cola.extension.register;\n\nimport com.alibaba.cola.extension.Extension;\nimport com.alibaba.cola.extension.ExtensionPointI;\nimport com.alibaba.cola.extension.Extensions;\nimport jakarta.annotation.PostConstruct;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.stereotype.Component;\n\n\nimport java.util.Map;\n\n/**\n * ExtensionBootstrap\n *\n * @author Frank Zhang\n * @date 2020-06-18 7:55 PM\n */\n@Slf4j\n@Component\npublic class ExtensionBootstrap implements ApplicationContextAware {\n\n    @Resource\n    private ExtensionRegister extensionRegister;\n\n    private ApplicationContext applicationContext;\n\n    @PostConstruct\n    public void init(){\n        Map<String, ExtensionPointI> extMap = applicationContext.getBeansOfType(ExtensionPointI.class);\n        for (ExtensionPointI ext : extMap.values()) {\n            if (ext.getClass().isAnnotationPresent(Extension.class)) {\n                extensionRegister.doRegistration(ext);\n            }else if (ext.getClass().isAnnotationPresent(Extensions.class)){\n                extensionRegister.doRegistrationExtensions(ext);\n            }else {\n                log.error(\"There is no annotation for @Extension or @Extension on this extension class:{}\" , ext.getClass());\n            }\n        }\n    }\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        this.applicationContext = applicationContext;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/register/ExtensionRegister.java",
    "content": "/*\n * Copyright 2017 Alibaba.com All right reserved. This software is the\n * confidential and proprietary information of Alibaba.com (\"Confidential\n * Information\"). You shall not disclose such Confidential Information and shall\n * use it only in accordance with the terms of the license agreement you entered\n * into with Alibaba.com.\n */\npackage com.alibaba.cola.extension.register;\n\nimport com.alibaba.cola.extension.*;\nimport jakarta.annotation.Resource;\nimport org.springframework.aop.support.AopUtils;\nimport org.springframework.core.annotation.AnnotatedElementUtils;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * ExtensionRegister\n *\n * @author fulan.zjf 2017-11-05\n */\n@Component\npublic class ExtensionRegister {\n\n    /**\n     * 扩展点接口名称不合法\n     */\n    private static final String EXTENSION_INTERFACE_NAME_ILLEGAL = \"extension_interface_name_illegal\";\n    /**\n     * 扩展点不合法\n     */\n    private static final String EXTENSION_ILLEGAL = \"extension_illegal\";\n    /**\n     * 扩展点定义重复\n     */\n    private static final String EXTENSION_DEFINE_DUPLICATE = \"extension_define_duplicate\";\n\n    @Resource\n    private ExtensionRepository extensionRepository;\n\n    public final static String EXTENSION_EXTPT_NAMING = \"ExtPt\";\n\n\n    public void doRegistration(ExtensionPointI extensionObject) {\n        Class<?> extensionClz = extensionObject.getClass();\n        if (AopUtils.isAopProxy(extensionObject)) {\n            extensionClz = AopUtils.getTargetClass(extensionObject);\n        }\n        Extension extensionAnn = AnnotatedElementUtils.findMergedAnnotation(extensionClz, Extension.class);\n        BizScenario bizScenario = BizScenario.valueOf(extensionAnn.bizId(), extensionAnn.useCase(), extensionAnn.scenario());\n        ExtensionCoordinate extensionCoordinate = new ExtensionCoordinate(calculateExtensionPoint(extensionClz), bizScenario.getUniqueIdentity());\n        ExtensionPointI preVal = extensionRepository.getExtensionRepo().put(extensionCoordinate, extensionObject);\n        if (preVal != null) {\n            String errMessage = \"Duplicate registration is not allowed for :\" + extensionCoordinate;\n            throw new ExtensionException(EXTENSION_DEFINE_DUPLICATE, errMessage);\n        }\n    }\n\n    public void doRegistrationExtensions(ExtensionPointI extensionObject){\n        Class<?> extensionClz = extensionObject.getClass();\n        if (AopUtils.isAopProxy(extensionObject)) {\n            extensionClz = ClassUtils.getUserClass(extensionObject);\n        }\n\n        Extensions extensionsAnnotation = AnnotationUtils.findAnnotation(extensionClz, Extensions.class);\n\n        //Support multiple extensions registration\n        String[] bizIds = extensionsAnnotation.bizId();\n        String[] useCases = extensionsAnnotation.useCase();\n        String[] scenarios = extensionsAnnotation.scenario();\n        for (String bizId : bizIds) {\n            for (String useCase : useCases) {\n                for (String scenario : scenarios) {\n                    BizScenario bizScenario = BizScenario.valueOf(bizId, useCase, scenario);\n                    ExtensionCoordinate extensionCoordinate = new ExtensionCoordinate(calculateExtensionPoint(extensionClz), bizScenario.getUniqueIdentity());\n                    ExtensionPointI preVal = extensionRepository.getExtensionRepo().put(extensionCoordinate, extensionObject);\n                    if (preVal != null) {\n                        String errMessage = \"Duplicate registration is not allowed for :\" + extensionCoordinate;\n                        throw new ExtensionException(EXTENSION_DEFINE_DUPLICATE, errMessage);\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * @param targetClz\n     * @return\n     */\n    private String calculateExtensionPoint(Class<?> targetClz) {\n        Class<?>[] interfaces = ClassUtils.getAllInterfacesForClass(targetClz);\n        if (interfaces == null || interfaces.length == 0) {\n            throw new ExtensionException(EXTENSION_ILLEGAL, \"Please assign a extension point interface for \" + targetClz);\n        }\n        for (Class intf : interfaces) {\n            String extensionPoint = intf.getSimpleName();\n            if (extensionPoint.contains(EXTENSION_EXTPT_NAMING)) {\n                return intf.getName();\n            }\n        }\n        String errMessage = \"Your name of ExtensionPoint for \" + targetClz +\n                \" is not valid, must be end of \" + EXTENSION_EXTPT_NAMING;\n        throw new ExtensionException(EXTENSION_INTERFACE_NAME_ILLEGAL, errMessage);\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.boot.autoconfigure.EnableAutoConfiguration = com.alibaba.cola.extension.ExtensionAutoConfiguration\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/Application.java",
    "content": "package com.alibaba.cola.extension;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.ComponentScan;\n\n/**\n * Application\n *\n * @author Frank Zhang\n * @date 2020-11-10 3:58 PM\n */\n@SpringBootApplication\n@ComponentScan(basePackages = \"com.alibaba.cola\")\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/ExtensionTest.java",
    "content": "package com.alibaba.cola.extension;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.extension.customer.client.AddCustomerCmd;\nimport com.alibaba.cola.extension.customer.client.Constants;\nimport com.alibaba.cola.extension.customer.client.CustomerDTO;\nimport com.alibaba.cola.extension.customer.client.CustomerServiceI;\nimport com.alibaba.cola.extension.customer.domain.CustomerType;\nimport jakarta.annotation.Resource;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n\n\n/**\n * ExtensionTest\n *\n * @author Frank Zhang\n * @date 2020-11-14 2:55 PM\n */\n@SpringBootTest(classes = Application.class)\npublic class ExtensionTest {\n    @Resource\n    private CustomerServiceI customerService;\n\n    @Test\n    public void testBiz1UseCase1Scenario1AddCustomerSuccess(){\n        //1. Prepare\n        AddCustomerCmd addCustomerCmd = new AddCustomerCmd();\n        CustomerDTO customerDTO = new CustomerDTO();\n        customerDTO.setCompanyName(\"alibaba\");\n        customerDTO.setSource(Constants.SOURCE_RFQ);\n        customerDTO.setCustomerType(CustomerType.IMPORTANT);\n        addCustomerCmd.setCustomerDTO(customerDTO);\n        BizScenario scenario = BizScenario.valueOf(Constants.BIZ_1, Constants.USE_CASE_1, Constants.SCENARIO_1);\n        addCustomerCmd.setBizScenario(scenario);\n\n        //2. Execute\n        Response response = customerService.addCustomer(addCustomerCmd);\n\n        //3. Expect Success\n        Assertions.assertTrue(response.isSuccess());\n    }\n\n    @Test\n    public void testBiz1UseCase1AddCustomerSuccess(){\n        //1. Prepare\n        AddCustomerCmd addCustomerCmd = new AddCustomerCmd();\n        CustomerDTO customerDTO = new CustomerDTO();\n        customerDTO.setCompanyName(\"alibaba\");\n        customerDTO.setSource(Constants.SOURCE_RFQ);\n        customerDTO.setCustomerType(CustomerType.IMPORTANT);\n        addCustomerCmd.setCustomerDTO(customerDTO);\n        BizScenario scenario = BizScenario.valueOf(Constants.BIZ_1, Constants.USE_CASE_1);\n        addCustomerCmd.setBizScenario(scenario);\n\n        //2. Execute\n        Response response = customerService.addCustomer(addCustomerCmd);\n\n        //3. Expect Success\n        Assertions.assertTrue(response.isSuccess());\n    }\n\n    @Test\n    public void testBiz1AddCustomerSuccess(){\n        //1. Prepare\n        AddCustomerCmd addCustomerCmd = new AddCustomerCmd();\n        CustomerDTO customerDTO = new CustomerDTO();\n        customerDTO.setCompanyName(\"jingdong\");\n        customerDTO.setSource(Constants.SOURCE_RFQ);\n        customerDTO.setCustomerType(CustomerType.IMPORTANT);\n        addCustomerCmd.setCustomerDTO(customerDTO);\n        BizScenario scenario = BizScenario.valueOf(Constants.BIZ_1);\n        addCustomerCmd.setBizScenario(scenario);\n\n        //2. Execute\n        Response response = customerService.addCustomer(addCustomerCmd);\n\n        //3. Expect Success\n        Assertions.assertTrue(response.isSuccess());\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/MultiCoordinateTests.java",
    "content": "package com.alibaba.cola.extension;\n\nimport com.alibaba.cola.extension.customer.app.extensionpoint.StatusNameConvertorExtPt;\n\nimport jakarta.annotation.Resource;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n\n/**\n * 多坐标测试\n *\n * @author wangguoqiang wrote on 2022/10/10 14:54\n * @version 1.0\n */\n@SpringBootTest(classes = Application.class)\npublic class MultiCoordinateTests {\n\n\n    @Resource\n    private ExtensionExecutor extensionExecutor;\n\n\n    @Test\n    public void testMultiCoordinate() {\n        BizScenario bizScenario1 = BizScenario.valueOf(\"Samsung\", \"order\", \"scenario1\");\n        BizScenario bizScenario2 = BizScenario.valueOf(\"Samsung\", \"order\", \"scenario2\");\n        BizScenario bizScenario3 = BizScenario.valueOf(\"Samsung\", \"parts\", \"scenario1\");\n        BizScenario bizScenario4 = BizScenario.valueOf(\"Samsung\", \"parts\", \"scenario2\");\n        BizScenario bizScenario5 = BizScenario.valueOf(\"Motorola\", \"order\", \"scenario1\");\n        BizScenario bizScenario6 = BizScenario.valueOf(\"Motorola\", \"order\", \"scenario2\");\n        BizScenario bizScenario7 = BizScenario.valueOf(\"Motorola\", \"parts\", \"scenario1\");\n        BizScenario bizScenario8 = BizScenario.valueOf(\"Motorola\", \"parts\", \"scenario2\");\n        String name1 = extensionExecutor.execute(StatusNameConvertorExtPt.class, bizScenario1, pt -> pt.statusNameConvertor(1));\n        String name2 = extensionExecutor.execute(StatusNameConvertorExtPt.class, bizScenario2, pt -> pt.statusNameConvertor(2));\n        String name3 = extensionExecutor.execute(StatusNameConvertorExtPt.class, bizScenario3, pt -> pt.statusNameConvertor(3));\n        String name4 = extensionExecutor.execute(StatusNameConvertorExtPt.class, bizScenario4, pt -> pt.statusNameConvertor(4));\n        String name5 = extensionExecutor.execute(StatusNameConvertorExtPt.class, bizScenario5, pt -> pt.statusNameConvertor(5));\n        String name6 = extensionExecutor.execute(StatusNameConvertorExtPt.class, bizScenario6, pt -> pt.statusNameConvertor(6));\n        String name7 = extensionExecutor.execute(StatusNameConvertorExtPt.class, bizScenario7, pt -> pt.statusNameConvertor(7));\n        String name8 = extensionExecutor.execute(StatusNameConvertorExtPt.class, bizScenario8, pt -> pt.statusNameConvertor(8));\n\n        Assertions.assertEquals(\"one\",name1);\n        Assertions.assertEquals(\"two\",name2);\n        Assertions.assertEquals(\"three\",name3);\n        Assertions.assertEquals(\"four\",name4);\n        Assertions.assertEquals(\"five\",name5);\n        Assertions.assertEquals(\"six\",name6);\n        Assertions.assertEquals(\"seven\",name7);\n        Assertions.assertEquals(\"eight\",name8);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/AddCustomerCmdExe.java",
    "content": "package com.alibaba.cola.extension.customer.app;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.extension.ExtensionExecutor;\nimport com.alibaba.cola.extension.customer.app.extensionpoint.AddCustomerValidatorExtPt;\nimport com.alibaba.cola.extension.customer.app.extensionpoint.CustomerConvertorExtPt;\nimport com.alibaba.cola.extension.customer.client.AddCustomerCmd;\nimport com.alibaba.cola.extension.customer.domain.CustomerEntity;\nimport com.alibaba.cola.extension.customer.infrastructure.DomainEventPublisher;\nimport jakarta.annotation.Resource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n/**\n * AddCustomerCmdExe\n *\n * @author Frank Zhang 2018-01-06 7:48 PM\n */\n@Component\npublic class AddCustomerCmdExe {\n\n    private Logger logger = LoggerFactory.getLogger(AddCustomerCmd.class);\n\n    @Resource\n    private ExtensionExecutor extensionExecutor;\n\n    @Resource\n    private DomainEventPublisher domainEventPublisher;\n\n\n    public Response execute(AddCustomerCmd cmd) {\n        logger.info(\"Start processing command:\" + cmd);\n\n        //validation\n        extensionExecutor.executeVoid(AddCustomerValidatorExtPt.class, cmd.getBizScenario(), extension -> extension.validate(cmd));\n\n        //Convert CO to Entity\n        CustomerEntity customerEntity = extensionExecutor.execute(CustomerConvertorExtPt.class, cmd.getBizScenario(), extension -> extension.clientToEntity(cmd));\n\n        //Call Domain Entity for business logic processing\n        logger.info(\"Call Domain Entity for business logic processing...\"+customerEntity);\n        customerEntity.addNewCustomer();\n\n        //domainEventPublisher.publish(new CustomerCreatedEvent());\n        logger.info(\"End processing command:\" + cmd);\n        return Response.buildSuccess();\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/CustomerCreatedEventHandler.java",
    "content": "package com.alibaba.cola.extension.customer.app;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.extension.customer.client.CustomerCreatedEvent;\n\n/**\n * CustomerCreatedEventHandler\n *\n * @author Frank Zhang\n * @date 2020-06-22 7:00 PM\n */\npublic class CustomerCreatedEventHandler {\n\n    public Response execute(CustomerCreatedEvent customerCreatedEvent) {\n        System.out.println(\"customerCreatedEvent processed\");\n        return null;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/CustomerServiceImpl.java",
    "content": "package com.alibaba.cola.extension.customer.app;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.dto.SingleResponse;\nimport com.alibaba.cola.extension.customer.client.AddCustomerCmd;\nimport com.alibaba.cola.extension.customer.client.CustomerDTO;\nimport com.alibaba.cola.extension.customer.client.CustomerServiceI;\nimport com.alibaba.cola.extension.customer.client.GetOneCustomerQry;\nimport jakarta.annotation.Resource;\nimport org.springframework.stereotype.Service;\n\n/**\n * CustomerServiceImpl\n *\n * @author Frank Zhang 2018-01-06 7:40 PM\n */\n@Service\npublic class CustomerServiceImpl implements CustomerServiceI {\n\n    @Resource\n    private AddCustomerCmdExe addCustomerCmdExe;\n\n    @Resource\n    private GetOneCustomerQryExe getOneCustomerQryExe;\n\n\n    @Override\n    public Response addCustomer(AddCustomerCmd addCustomerCmd) {\n        return addCustomerCmdExe.execute(addCustomerCmd);\n    }\n\n    @Override\n    public SingleResponse<CustomerDTO> getCustomer(GetOneCustomerQry getOneCustomerQry) {\n        return getOneCustomerQryExe.execute(getOneCustomerQry);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/GetOneCustomerQryExe.java",
    "content": "package com.alibaba.cola.extension.customer.app;\n\nimport com.alibaba.cola.dto.SingleResponse;\nimport com.alibaba.cola.extension.customer.client.GetOneCustomerQry;\nimport org.springframework.stereotype.Component;\n\n/**\n * GetOneCustomerQryExe\n *\n * @author Frank Zhang\n * @date 2020-06-22 5:12 PM\n */\n@Component\npublic class GetOneCustomerQryExe {\n\n    public SingleResponse execute(GetOneCustomerQry getOneCustomerQry){\n        return null;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/extension/AddCustomerBiz1UseCase1Scenario1Validator.java",
    "content": "package com.alibaba.cola.extension.customer.app.extension;\n\nimport com.alibaba.cola.extension.Extension;\nimport com.alibaba.cola.extension.customer.client.AddCustomerCmd;\nimport com.alibaba.cola.extension.customer.client.Constants;\nimport com.alibaba.cola.extension.customer.app.extensionpoint.AddCustomerValidatorExtPt;\n\n/**\n * AddCustomerBiz1UseCase1Scenario1Validator\n *\n * @author Frank Zhang\n * @date 2020-08-20 1:01 PM\n */\n@Extension(bizId = Constants.BIZ_1, useCase = Constants.USE_CASE_1, scenario = Constants.SCENARIO_1)\npublic class AddCustomerBiz1UseCase1Scenario1Validator implements AddCustomerValidatorExtPt {\n    public void validate(AddCustomerCmd addCustomerCmd) {\n        System.out.println(\"Do validation for Biz_One's Use_Case_One's Scenario_One\");\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/extension/AddCustomerBiz1UseCase1Validator.java",
    "content": "package com.alibaba.cola.extension.customer.app.extension;\n\nimport com.alibaba.cola.extension.Extension;\nimport com.alibaba.cola.extension.customer.client.AddCustomerCmd;\nimport com.alibaba.cola.extension.customer.client.Constants;\nimport com.alibaba.cola.extension.customer.app.extensionpoint.AddCustomerValidatorExtPt;\n\n/**\n * AddCustomerBiz1UseCase1Validator\n *\n * @author Frank Zhang\n * @date 2020-08-20 12:58 PM\n */\n@Extension(bizId = Constants.BIZ_1, useCase = Constants.USE_CASE_1)\npublic class AddCustomerBiz1UseCase1Validator implements AddCustomerValidatorExtPt {\n    public void validate(AddCustomerCmd addCustomerCmd) {\n        System.out.println(\"Do validation for Biz_One's Use_Case_One\");\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/extension/AddCustomerBizOneValidator.java",
    "content": "package com.alibaba.cola.extension.customer.app.extension;\n\nimport com.alibaba.cola.exception.BizException;\nimport com.alibaba.cola.extension.Extension;\nimport com.alibaba.cola.extension.customer.client.AddCustomerCmd;\nimport com.alibaba.cola.extension.customer.client.Constants;\nimport com.alibaba.cola.extension.customer.domain.CustomerType;\nimport com.alibaba.cola.extension.customer.app.extensionpoint.AddCustomerValidatorExtPt;\n\n/**\n * AddCustomerBizOneValidator\n *\n * @author Frank Zhang\n * @date 2018-01-07 1:31 AM\n */\n@Extension(bizId = Constants.BIZ_1)\npublic class AddCustomerBizOneValidator implements AddCustomerValidatorExtPt {\n\n    public void validate(AddCustomerCmd addCustomerCmd) {\n        //For BIZ TWO CustomerTYpe could not be VIP\n        if(CustomerType.VIP == addCustomerCmd.getCustomerDTO().getCustomerType())\n            throw new BizException(\"Customer Type could not be VIP for Biz One\");\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/extension/AddCustomerBizTwoValidator.java",
    "content": "package com.alibaba.cola.extension.customer.app.extension;\n\nimport com.alibaba.cola.exception.BizException;\nimport com.alibaba.cola.extension.Extension;\nimport com.alibaba.cola.extension.customer.client.AddCustomerCmd;\nimport com.alibaba.cola.extension.customer.client.Constants;\nimport com.alibaba.cola.extension.customer.app.extensionpoint.AddCustomerValidatorExtPt;\n\n/**\n * AddCustomerBizTwoValidator\n *\n * @author Frank Zhang\n * @date 2018-01-07 1:31 AM\n */\n@Extension(bizId = Constants.BIZ_2)\npublic class AddCustomerBizTwoValidator implements AddCustomerValidatorExtPt {\n\n    public void validate(AddCustomerCmd addCustomerCmd) {\n        //For BIZ TWO CustomerTYpe could not be null\n        if (addCustomerCmd.getCustomerDTO().getCustomerType() == null)\n            throw new BizException(\"CustomerType could not be null\");\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/extension/CustomerBizOneConvertorExt.java",
    "content": "package com.alibaba.cola.extension.customer.app.extension;\n\nimport com.alibaba.cola.extension.Extension;\nimport com.alibaba.cola.extension.customer.app.extensionpoint.CustomerConvertorExtPt;\nimport com.alibaba.cola.extension.customer.client.AddCustomerCmd;\nimport com.alibaba.cola.extension.customer.client.Constants;\nimport com.alibaba.cola.extension.customer.client.CustomerDTO;\nimport com.alibaba.cola.extension.customer.domain.CustomerEntity;\nimport com.alibaba.cola.extension.customer.domain.SourceType;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * CustomerBizOneConvertorExt\n *\n * @author Frank Zhang\n * @date 2018-01-07 3:05 AM\n */\n@Extension(bizId = Constants.BIZ_1)\npublic class CustomerBizOneConvertorExt  implements CustomerConvertorExtPt {\n\n    @Autowired\n    private CustomerConvertor customerConvertor;//Composite basic convertor to do basic conversion\n\n    @Override\n    public CustomerEntity clientToEntity(AddCustomerCmd addCustomerCmd){\n        CustomerEntity customerEntity = customerConvertor.clientToEntity(addCustomerCmd);\n        CustomerDTO customerDTO =addCustomerCmd.getCustomerDTO();\n        //In this business, AD and RFQ are regarded as different source\n        if(Constants.SOURCE_AD.equals(customerDTO.getSource()))\n        {\n            customerEntity.setSourceType(SourceType.AD);\n        }\n        if (Constants.SOURCE_RFQ.equals(customerDTO.getSource())){\n            customerEntity.setSourceType(SourceType.RFQ);\n        }\n        return customerEntity;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/extension/CustomerBizTwoConvertorExt.java",
    "content": "package com.alibaba.cola.extension.customer.app.extension;\n\nimport com.alibaba.cola.extension.Extension;\nimport com.alibaba.cola.extension.customer.app.extensionpoint.CustomerConvertorExtPt;\nimport com.alibaba.cola.extension.customer.client.AddCustomerCmd;\nimport com.alibaba.cola.extension.customer.client.Constants;\nimport com.alibaba.cola.extension.customer.domain.CustomerEntity;\nimport com.alibaba.cola.extension.customer.domain.SourceType;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * CustomerBizTwoConvertorExt\n *\n * @author Frank Zhang\n * @date 2018-01-07 3:05 AM\n */\n@Extension(bizId = Constants.BIZ_2)\npublic class CustomerBizTwoConvertorExt implements CustomerConvertorExtPt {\n\n    @Autowired\n    private CustomerConvertor customerConvertor;//Composite basic convertor to do basic conversion\n\n    @Override\n    public CustomerEntity clientToEntity(AddCustomerCmd addCustomerCmd){\n        CustomerEntity customerEntity = customerConvertor.clientToEntity(addCustomerCmd);\n        //In this business, if customers from RFQ and Advertisement are both regarded as Advertisement\n        if(Constants.SOURCE_AD.equals(addCustomerCmd.getCustomerDTO().getSource()) || Constants.SOURCE_RFQ.equals(addCustomerCmd.getCustomerDTO().getSource()))\n        {\n            customerEntity.setSourceType(SourceType.AD);\n        }\n        return customerEntity;\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/extension/CustomerConvertor.java",
    "content": "package com.alibaba.cola.extension.customer.app.extension;\n\nimport com.alibaba.cola.domain.ApplicationContextHelper;\nimport com.alibaba.cola.extension.customer.client.AddCustomerCmd;\nimport com.alibaba.cola.extension.customer.client.CustomerDTO;\nimport com.alibaba.cola.extension.customer.domain.CustomerEntity;\nimport org.springframework.stereotype.Component;\n\n/**\n * CustomerConvertor\n *\n * @author Frank Zhang\n * @date 2018-01-07 3:08 AM\n */\n@Component\npublic class CustomerConvertor{\n\n    public CustomerEntity clientToEntity(Object clientObject){\n        AddCustomerCmd addCustomerCmd = (AddCustomerCmd)clientObject;\n        CustomerDTO customerDTO =addCustomerCmd.getCustomerDTO();\n        CustomerEntity customerEntity = (CustomerEntity) ApplicationContextHelper.getBean(CustomerEntity.class);\n        customerEntity.setCompanyName(customerDTO.getCompanyName());\n        customerEntity.setCustomerType(customerDTO.getCustomerType());\n        customerEntity.setBizScenario(addCustomerCmd.getBizScenario());\n        return customerEntity;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/extension/StatusNameConvertorExt.java",
    "content": "package com.alibaba.cola.extension.customer.app.extension;\n\nimport com.alibaba.cola.extension.Extensions;\nimport com.alibaba.cola.extension.customer.app.extensionpoint.StatusNameConvertorExtPt;\n\nimport java.util.HashMap;\n\n/**\n * @author wangguoqiang wrote on 2022/10/10 14:39\n * @version 1.0\n */\n@Extensions(bizId = {\"Samsung\", \"Motorola\"}, useCase = {\"order\", \"parts\"}, scenario = {\"scenario1\", \"scenario2\"})\npublic class StatusNameConvertorExt implements StatusNameConvertorExtPt {\n\n    /**\n     * In real business scenarios, the business status is usually represented by numbers.\n     * In some places, the number needs to be mapped to the real status name. For ease of understanding here,\n     * the status name is the number in English\n     */\n    HashMap<Integer, String> map = new HashMap<Integer, String>() {{\n        put(1, \"one\");\n        put(2, \"two\");\n        put(3, \"three\");\n        put(4, \"four\");\n        put(5, \"five\");\n        put(6, \"six\");\n        put(7, \"seven\");\n        put(8, \"eight\");\n    }};\n\n    @Override\n    public String statusNameConvertor(Integer statusCode) {\n        return map.getOrDefault(statusCode, \"unknow\");\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/extensionpoint/AddCustomerValidatorExtPt.java",
    "content": "package com.alibaba.cola.extension.customer.app.extensionpoint;\n\nimport com.alibaba.cola.extension.ExtensionPointI;\nimport com.alibaba.cola.extension.customer.client.AddCustomerCmd;\n\n/**\n * AddCustomerValidatorExtPt\n *\n * @author Frank Zhang\n * @date 2018-01-07 1:27 AM\n */\npublic interface AddCustomerValidatorExtPt extends ExtensionPointI {\n\n    public void validate(AddCustomerCmd addCustomerCmd);\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/extensionpoint/CustomerConvertorExtPt.java",
    "content": "package com.alibaba.cola.extension.customer.app.extensionpoint;\n\nimport com.alibaba.cola.extension.ExtensionPointI;\nimport com.alibaba.cola.extension.customer.client.AddCustomerCmd;\nimport com.alibaba.cola.extension.customer.domain.CustomerEntity;\n\n/**\n * CustomerConvertorExtPt\n *\n * @author Frank Zhang\n * @date 2018-01-07 2:37 AM\n */\npublic interface CustomerConvertorExtPt extends ExtensionPointI {\n\n    public CustomerEntity clientToEntity(AddCustomerCmd addCustomerCmd);\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/app/extensionpoint/StatusNameConvertorExtPt.java",
    "content": "package com.alibaba.cola.extension.customer.app.extensionpoint;\n\nimport com.alibaba.cola.extension.ExtensionPointI;\n\n/**\n * This extension point supports state transition operations of multiple manufacturers and different business lines\n *\n * @author wangguoqiang wrote on 2022/10/10 14:37\n * @version 1.0\n */\npublic interface StatusNameConvertorExtPt extends ExtensionPointI {\n    String statusNameConvertor(Integer statusCode);\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/client/AddCustomerCmd.java",
    "content": "package com.alibaba.cola.extension.customer.client;\n\n\nimport com.alibaba.cola.dto.Command;\nimport com.alibaba.cola.extension.BizScenario;\nimport lombok.Data;\n\n/**\n * AddCustomerCmd\n *\n * @author Frank Zhang 2018-01-06 7:28 PM\n */\n@Data\npublic class AddCustomerCmd extends Command {\n\n    private CustomerDTO customerDTO;\n\n    private String biz;\n\n    private BizScenario bizScenario;\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/client/Constants.java",
    "content": "package com.alibaba.cola.extension.customer.client;\n\n/**\n * Constants\n *\n * @author Frank Zhang\n * @date 2018-01-07 1:23 AM\n */\npublic class Constants {\n\n    public static final String BIZ_1 = \"BIZ_ONE\";\n    public static final String USE_CASE_1 = \"USE_CASE_ONE\";\n    public static final String SCENARIO_1 = \"SCENARIO_ONE\";\n    public static final String BIZ_2 = \"BIZ_TWO\";\n\n\n    public static final String SOURCE_AD = \"Advertisement\";\n    public static final String SOURCE_WB = \"Web site\";\n    public static final String SOURCE_RFQ = \"Request For Quota\";\n    public static final String SOURCE_MARKETING = \"Marketing\";\n    public static final String SOURCE_OFFLINE = \"Off Line\";\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/client/CustomerCreatedEvent.java",
    "content": "package com.alibaba.cola.extension.customer.client;\n\n/**\n * CustomerCreatedEvent\n *\n * @author Frank Zhang\n * @date 2020-06-22 6:59 PM\n */\npublic class CustomerCreatedEvent {\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/client/CustomerDTO.java",
    "content": "package com.alibaba.cola.extension.customer.client;\n\nimport com.alibaba.cola.dto.DTO;\nimport com.alibaba.cola.extension.customer.domain.CustomerType;\n\n\n/**\n * CustomerDTO\n *\n * @author Frank Zhang 2018-01-06 7:30 PM\n */\npublic class CustomerDTO extends DTO {\n\n    private String companyName;\n    private String source;  //advertisement, p4p, RFQ, ATM\n    private CustomerType customerType; //potential, intentional, important, vip\n\n    public String getCompanyName() {\n        return companyName;\n    }\n\n    public void setCompanyName(String companyName) {\n        this.companyName = companyName;\n    }\n\n    public String getSource() {\n        return source;\n    }\n\n    public void setSource(String source) {\n        this.source = source;\n    }\n\n    public CustomerType getCustomerType() {\n        return customerType;\n    }\n\n    public void setCustomerType(CustomerType customerType) {\n        this.customerType = customerType;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/client/CustomerServiceI.java",
    "content": "package com.alibaba.cola.extension.customer.client;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.dto.SingleResponse;\n\n/**\n * CustomerServiceI\n *\n * @author Frank Zhang 2018-01-06 7:24 PM\n */\npublic interface CustomerServiceI {\n    public Response addCustomer(AddCustomerCmd addCustomerCmd);\n    public SingleResponse<CustomerDTO> getCustomer(GetOneCustomerQry getOneCustomerQry);\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/client/GetOneCustomerQry.java",
    "content": "package com.alibaba.cola.extension.customer.client;\n\nimport com.alibaba.cola.dto.Query;\n\n/**\n * GetOneCustomerQry\n *\n * @author Frank Zhang 2018-01-06 7:38 PM\n */\npublic class GetOneCustomerQry extends Query{\n    private long customerId;\n    private String companyName;\n\n    public long getCustomerId() {\n        return customerId;\n    }\n\n    public void setCustomerId(long customerId) {\n        this.customerId = customerId;\n    }\n\n    public String getCompanyName() {\n        return companyName;\n    }\n\n    public void setCompanyName(String companyName) {\n        this.companyName = companyName;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/domain/CustomerEntity.java",
    "content": "package com.alibaba.cola.extension.customer.domain;\n\nimport com.alibaba.cola.domain.Entity;\nimport com.alibaba.cola.extension.BizScenario;\nimport com.alibaba.cola.extension.ExtensionExecutor;\nimport com.alibaba.cola.extension.customer.domain.rule.CustomerRuleExtPt;\nimport com.alibaba.cola.extension.customer.infrastructure.CustomerRepository;\nimport lombok.Data;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * Customer Entity\n *\n * @author Frank Zhang\n * @date 2018-01-07 2:38 AM\n */\n@Entity\n@Data\npublic class CustomerEntity {\n\n    private String companyName;\n    private SourceType sourceType;\n    private CustomerType customerType;\n    private BizScenario bizScenario;\n\n    @Autowired\n    private CustomerRepository customerRepository;\n\n    @Autowired\n    private ExtensionExecutor extensionExecutor;\n\n    public CustomerEntity() {\n\n    }\n\n    public void addNewCustomer() {\n        //Add customer policy\n        extensionExecutor.execute(CustomerRuleExtPt.class,this.getBizScenario(), extension -> extension.addCustomerCheck(this));\n\n        //Persist customer\n        customerRepository.persist(this);\n\n    }\n\n    public String getCompanyName() {\n        return companyName;\n    }\n\n    public void setCompanyName(String companyName) {\n        this.companyName = companyName;\n    }\n\n    public SourceType getSourceType() {\n        return sourceType;\n    }\n\n    public void setSourceType(SourceType sourceType) {\n        this.sourceType = sourceType;\n    }\n\n    public CustomerType getCustomerType() {\n        return customerType;\n    }\n\n    public void setCustomerType(CustomerType customerType) {\n        this.customerType = customerType;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/domain/CustomerType.java",
    "content": "package com.alibaba.cola.extension.customer.domain;\n\n/**\n * CustomerType\n *\n * @author Frank Zhang 2018-01-06 7:35 PM\n */\npublic enum CustomerType {\n    POTENTIAL,\n    INTENTIONAL,\n    IMPORTANT,\n    VIP;\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/domain/SourceType.java",
    "content": "package com.alibaba.cola.extension.customer.domain;\n\n/**\n * SourceType\n *\n * @author Frank Zhang\n * @date 2018-01-07 3:02 AM\n */\npublic enum SourceType {\n    AD, //Advertisement 广告\n    WB, // Web site 网站\n    RFQ; // Request For Quota 询盘\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/domain/rule/CustomerBizOneRuleExt.java",
    "content": "package com.alibaba.cola.extension.customer.domain.rule;\n\nimport com.alibaba.cola.exception.BizException;\nimport com.alibaba.cola.extension.Extension;\nimport com.alibaba.cola.extension.customer.client.Constants;\nimport com.alibaba.cola.extension.customer.domain.CustomerEntity;\nimport com.alibaba.cola.extension.customer.domain.SourceType;\n\n/**\n * CustomerBizOneRuleExt\n *\n * @author Frank Zhang\n * @date 2018-01-07 12:10 PM\n */\n@Extension(bizId = Constants.BIZ_1)\npublic class CustomerBizOneRuleExt implements CustomerRuleExtPt{\n\n    @Override\n    public boolean addCustomerCheck(CustomerEntity customerEntity) {\n        if(SourceType.AD == customerEntity.getSourceType()){\n            throw new BizException(\"Sorry, Customer from advertisement can not be added in this period\");\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/domain/rule/CustomerBizTwoRuleExt.java",
    "content": "package com.alibaba.cola.extension.customer.domain.rule;\n\nimport com.alibaba.cola.extension.Extension;\nimport com.alibaba.cola.extension.customer.client.Constants;\nimport com.alibaba.cola.extension.customer.domain.CustomerEntity;\n\n/**\n * CustomerBizTwoRuleExt\n *\n * @author Frank Zhang\n * @date 2018-01-07 12:10 PM\n */\n@Extension(bizId = Constants.BIZ_2)\npublic class CustomerBizTwoRuleExt implements CustomerRuleExtPt{\n\n    @Override\n    public boolean addCustomerCheck(CustomerEntity customerEntity) {\n        //Any Customer can be added\n        return true;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/domain/rule/CustomerRuleExtPt.java",
    "content": "package com.alibaba.cola.extension.customer.domain.rule;\n\nimport com.alibaba.cola.extension.ExtensionPointI;\nimport com.alibaba.cola.extension.customer.domain.CustomerEntity;\n\n/**\n * CustomerRuleExtPt\n *\n * @author Frank Zhang\n * @date 2018-01-07 12:03 PM\n */\npublic interface CustomerRuleExtPt extends ExtensionPointI {\n\n    //Different business check for different biz\n    public boolean addCustomerCheck(CustomerEntity customerEntity);\n\n    //Different upgrade policy for different biz\n    default public void customerUpgradePolicy(CustomerEntity customerEntity){\n        //Nothing special\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/infrastructure/CustomerDO.java",
    "content": "package com.alibaba.cola.extension.customer.infrastructure;\n\n/**\n * CustomerDO\n *\n * @author Frank Zhang\n * @date 2018-01-08 1:45 PM\n */\n\npublic class CustomerDO implements java.io.Serializable {\n    private String customerId;\n    private String memberId;\n    private String globalId;\n    private String companyName;\n    private String source;\n    private String companyType;\n\n    public String getCustomerId() {\n        return customerId;\n    }\n\n    public void setCustomerId(String customerId) {\n        this.customerId = customerId;\n    }\n\n    public String getMemberId() {\n        return memberId;\n    }\n\n    public void setMemberId(String memberId) {\n        this.memberId = memberId;\n    }\n\n    public String getGlobalId() {\n        return globalId;\n    }\n\n    public void setGlobalId(String globalId) {\n        this.globalId = globalId;\n    }\n\n    public String getCompanyName() {\n        return companyName;\n    }\n\n    public void setCompanyName(String companyName) {\n        this.companyName = companyName;\n    }\n\n    public String getSource() {\n        return source;\n    }\n\n    public void setSource(String source) {\n        this.source = source;\n    }\n\n    public String getCompanyType() {\n        return companyType;\n    }\n\n    public void setCompanyType(String companyType) {\n        this.companyType = companyType;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/infrastructure/CustomerRepository.java",
    "content": "package com.alibaba.cola.extension.customer.infrastructure;\n\nimport com.alibaba.cola.extension.customer.domain.CustomerEntity;\nimport org.springframework.stereotype.Repository;\n\n/**\n * CustomerRepository\n *\n * @author Frank Zhang\n * @date 2018-01-07 11:59 AM\n */\n@Repository\npublic class CustomerRepository {\n\n    public void persist(CustomerEntity customerEntity){\n        System.out.println(\"Persist customer to DB : \"+ customerEntity);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/customer/infrastructure/DomainEventPublisher.java",
    "content": "package com.alibaba.cola.extension.customer.infrastructure;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * DomainEventPublisher\n *\n * @author Frank Zhang\n * @date 2020-06-22 7:04 PM\n */\n@Component\npublic class DomainEventPublisher {\n/*    @Resource\n    private EventBusI eventBus;\n\n    public void publish(DomainEventI domainEvent) {\n        eventBus.fire(domainEvent);\n    }*/\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/register/CglibProxyFactory.java",
    "content": "package com.alibaba.cola.extension.register;\n\nimport java.lang.reflect.Method;\n\nimport org.springframework.cglib.proxy.Enhancer;\nimport org.springframework.cglib.proxy.MethodInterceptor;\nimport org.springframework.cglib.proxy.MethodProxy;\n\npublic class CglibProxyFactory {\n\n    public static <T> T createProxy(T object) {\n        Enhancer enhancer = new Enhancer();\n        enhancer.setSuperclass(object.getClass());\n        enhancer.setCallback(new ProxyCallback(object));\n        return (T) enhancer.create();\n    }\n\n    public static class ProxyCallback implements MethodInterceptor {\n\n        private Object target;\n    \n        public ProxyCallback(Object target) {\n            this.target = target;\n        }\n        \n        @Override\n        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {\n            System.out.println(\"ProxyObject::before\");\n            Object object = proxy.invoke(target, args);\n            System.out.println(\"ProxyObject::after\");\n            return object;\n        }\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/register/ExtensionRegisterTest.java",
    "content": "package com.alibaba.cola.extension.register;\n\nimport com.alibaba.cola.extension.BizScenario;\nimport com.alibaba.cola.extension.ExtensionException;\nimport com.alibaba.cola.extension.ExtensionExecutor;\nimport com.alibaba.cola.extension.Application;\nimport jakarta.annotation.Resource;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n@SpringBootTest(classes = Application.class)\npublic class ExtensionRegisterTest {\n\n    @Resource\n    private ExtensionRegister register;\n\n    @Resource\n    private ExtensionExecutor executor;\n\n    @Test\n    public void testDuplicateRegistration() {\n        // expect:\n        //Duplicate registration is not allowed for :ExtensionCoordinate\n        // [extensionPointName=com.alibaba.cola.extension.register.SomeExtPt, bizScenarioUniqueIdentity=A.#defaultUseCase#.#defaultScenario#]\n        Assertions.assertThrows(ExtensionException.class, ()->{\n            SomeExtPt extA = new SomeExtensionA();\n            register.doRegistration(extA);\n\n            executor.executeVoid(SomeExtPt.class, BizScenario.valueOf(\"A\"), SomeExtPt::doSomeThing);\n        });\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/register/SomeExtPt.java",
    "content": "package com.alibaba.cola.extension.register;\n\nimport com.alibaba.cola.extension.ExtensionPointI;\n\npublic interface SomeExtPt extends ExtensionPointI {\n    \n    public void doSomeThing();\n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/register/SomeExtensionA.java",
    "content": "package com.alibaba.cola.extension.register;\n\nimport com.alibaba.cola.extension.Extension;\n\nimport org.springframework.stereotype.Component;\n\n@Extension(bizId = \"A\")\n@Component\npublic class SomeExtensionA implements SomeExtPt {\n\n    @Override\n    public void doSomeThing() {\n        System.out.println(\"SomeExtensionA::doSomething\");\n    }\n\n}"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/java/com/alibaba/cola/extension/register/SomeExtensionB.java",
    "content": "package com.alibaba.cola.extension.register;\n\nimport com.alibaba.cola.extension.Extension;\n\nimport org.springframework.stereotype.Component;\n\n@Extension(bizId = \"B\")\n@Component\npublic class SomeExtensionB implements SomeExtPt {\n\n    @Override\n    public void doSomeThing() {\n        System.out.println(\"SomeExtensionB::doSomething\");\n    }\n    \n}\n"
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/resources/application.properties",
    "content": ""
  },
  {
    "path": "cola-components/cola-component-extension-starter/src/test/resources/logback-test.xml",
    "content": "<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <!--定义了两个appender，日志会通过往这两个appender里面写-->\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n\n    <!--应用日志-->\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n    <logger name=\"com.alibaba.cola.extension\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-components/cola-component-job/README.md",
    "content": "# 主要功能\n可持久化、分布式任务管理组件：\n1. 可同步，可异步\n2. 可持久化（redis，数据库）\n3. Job可重复执行，Step失败可回滚，Step可断点继续\n4. 支持批量任务\n\n# 核心领域概念\n6. JobLauncher：是任务启动器，通过它来启动任务，可以看做是程序的入口。\n7. BatchJobLauncher: 批量任务的处理入口\n8. BatchJob：批量任务，包含了多个JobInstance，一个JobInstance包含一个Job单例和一个ExecutionContext对象\n9. ExecutionContext: 任务执行的上下文，主要是用来承载用户传递的参数，以及不同Job、step之间传递参数\n10. Job: 代表着一个具体的任务。\n11. JobExecution: 代表一次具体的任务执行，可持久化，会管理任务执行状态。\n12. Step: 代表着一个具体的步骤，一个Job可以包含多个Step。在实际业务场景中，可能一个任务很复杂，这个时候可以将任务 拆分成多个step，分别对这些step 进行管理（将一个复杂任务简单化）。\n13. StepExecution： 代表一个具体的执行步骤，可持久化，会管理步骤的执行状态。\n14. JobRepository：批处理框架执行过程中的上下文（元数据）,有三种实现，1）通过内存来管理，2）通过Redis持久化，3）通过数据库持久化。\n\n# 详细设计解析\nhttps://blog.csdn.net/significantfrank/article/details/145314072\n\n# 如何使用\n1. 引入依赖cola-component-job依赖。注意：为了减少冲突，依赖的存储redis和database均为provided，需要使用方自己提供，SpringBoot版本最好在3.2.0以上\n2. 在SpringBoot的启动类上添加@EnableColaJob注解，该注解会扫描注册需要的bean，以及负责初始化JobRepository\n3. 默认的数据库初始化脚本是schema-mysql.sql。如果使用其他数据库，可通过application.yml配置文件中的cola.job.database.ddl-location属性来指定脚本路径\n"
  },
  {
    "path": "cola-components/cola-component-job/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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.alibaba.cola</groupId>\n    <artifactId>cola-component-job</artifactId>\n    <packaging>jar</packaging>\n    <name>${project.artifactId}:${project.version}</name>\n    <description>${project.artifactId}</description>\n    <version>1.0.0-SNAPSHOT</version>\n\n    <properties>\n        <maven.compiler.source>17</maven.compiler.source>\n        <maven.compiler.target>17</maven.compiler.target>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <spring.boot.version>3.2.9</spring.boot.version>\n    </properties>\n\n    <dependencies>\n        <!-- Lombok -->\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.34</version>\n            <scope>provided</scope>\n        </dependency>\n        <!-- Logback -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-jpa</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n        <!-- test start-->\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>8.0.33</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter-engine</artifactId>\n            <version>5.10.2</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-unittest</artifactId>\n            <version>5.0.0</version>\n            <exclusions>\n                <exclusion>\n                    <groupId>org.springframework.kafka</groupId>\n                    <artifactId>spring-kafka</artifactId>\n                </exclusion>\n                <exclusion>\n                    <groupId>org.springframework.kafka</groupId>\n                    <artifactId>spring-kafka-test</artifactId>\n                </exclusion>\n                <exclusion>\n                    <groupId>org.postgresql</groupId>\n                    <artifactId>postgresql</artifactId>\n                </exclusion>\n            </exclusions>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-test-container</artifactId>\n            <version>5.0.0</version>\n            <scope>test</scope>\n        </dependency>\n        <!-- test end-->\n    </dependencies>\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.8.1</version>\n                <configuration>\n                    <source>17</source>\n                    <target>17</target>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/BatchJobLauncher.java",
    "content": "package com.alibaba.cola.job;\n\nimport com.alibaba.cola.job.model.*;\nimport com.alibaba.cola.job.repository.JobRepository;\nimport lombok.extern.slf4j.Slf4j;\n\nimport org.springframework.util.CollectionUtils;\n\nimport java.time.LocalDateTime;\nimport java.util.Set;\n\n/**\n * 批量任务只支持单个Job的异步执行\n */\n@Slf4j\npublic class BatchJobLauncher {\n    public static BatchJobExecution execute(BatchJob batchJob) {\n        JobRepository jobRepository = batchJob.getJobRepository();\n        BatchJobExecution batchJobExecution = new BatchJobExecution(UuidGenerator.nextBatchJobId());\n        for (JobInstance jobInstance : batchJob.getJobInstances()) {\n            jobInstance.getExecutionContext().setBatchJobId(batchJobExecution.getBatchJobId());\n            String jobExecutionId = JobLauncher.executeAsync(jobInstance.getJob(), jobInstance.getExecutionContext(),\n                    batchJob.getExecutorService());\n            batchJobExecution.addJobExecution(jobExecutionId);\n        }\n        batchJobExecution.setStartTime(LocalDateTime.now());\n        batchJobExecution.setStatus(ExecutionStatus.STARTED);\n        batchJobExecution.setJobType(batchJob.getJobType());\n        jobRepository.saveBatchJobExecution(batchJobExecution);\n        log.info(\"[BatchJob] started: {}\", batchJobExecution);\n        return batchJobExecution;\n    }\n\n    public static boolean checkAndRefresh(BatchJob batchJob, String batchJobId) {\n        JobRepository jobRepository = batchJob.getJobRepository();\n        BatchJobExecution batchJobExecution = jobRepository.getBatchJobExecutionByBatchJobId(batchJobId);\n        Set<String> jobIds = batchJobExecution.getJobExecutionResults().keySet();\n        if (CollectionUtils.isEmpty(jobIds)) {\n            return false;\n        }\n        // 如果已经完成，就不用费时查看每个job的状态了\n        if (batchJobExecution.isBatchJobCompleted()) {\n            return true;\n        }\n        for (String jobId : jobIds) {\n            JobExecution jobExecution = jobRepository.getJobExecutionByJobId(jobId);\n            batchJobExecution.put(jobExecution.getJobId(), jobExecution.getExecutionStatus());\n        }\n        boolean isChildrenCompleted = batchJobExecution.isChildrenJobsCompleted();\n        if (isChildrenCompleted) {\n            // 所有子任务都已完成\n            batchJobExecution.setStatus(ExecutionStatus.COMPLETED);\n            batchJobExecution.setEndTime(LocalDateTime.now());\n        } else if (batchJobExecution.isFailed()) {\n            batchJobExecution.setStatus(ExecutionStatus.FAILED);\n        } else {\n            batchJobExecution.setStatus(ExecutionStatus.UNKNOWN);\n        }\n        jobRepository.updateBatchJobExecution(batchJobExecution);\n        return isChildrenCompleted;\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/ExecutionContext.java",
    "content": "package com.alibaba.cola.job;\n\nimport com.alibaba.cola.job.repository.JsonUtil;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport lombok.ToString;\n\nimport org.springframework.lang.Nullable;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n@ToString\npublic class ExecutionContext<T> {\n    /**\n     * 这是任务执行的主参数，通常是client自定义的类\n     */\n    private T param;\n\n    @JsonIgnore\n    private Object rawParam;\n\n    /**\n     * 为了灵活性，主参数之外的数据，放在extensions\n     */\n    @JsonProperty(\"extensions\")\n    private final Map<String, Object> extensions;\n\n\n    /**\n     * 这里是暂存数据到context的，不会进行持久化\n     */\n    @JsonIgnore\n    private final Map<String, Object> temp;\n\n    /**\n     * 当执行批量任务的时候，需要传入batchJobId\n     */\n    @JsonIgnore\n    private String batchJobId;\n\n    /**\n     * 当重复执行一个Job的时候，需要传入上一次执行的jobExecutionId\n     */\n    @JsonIgnore\n    private String jobId;\n\n    public T getParam() {\n        return (T) param;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public T getParam(Class<T> tClass) {\n        if (rawParam != null) {\n            return (T) rawParam;\n        }\n        if (param.getClass().equals(tClass)) {\n            rawParam = param;\n        }\n        rawParam = JsonUtil.decode(JsonUtil.encode(param), tClass);\n        return (T) rawParam;\n    }\n\n    public void setParam(T param) {\n        this.param = param;\n    }\n\n    public String getJobId() {\n        return jobId;\n    }\n\n    public void setJobId(String jobId) {\n        this.jobId = jobId;\n    }\n\n    public String getBatchJobId() {\n        return batchJobId;\n    }\n\n    public void setBatchJobId(String batchJobId) {\n        this.batchJobId = batchJobId;\n    }\n\n    public ExecutionContext() {\n        this.extensions = new ConcurrentHashMap();\n        this.temp = new ConcurrentHashMap<>();\n    }\n\n    public ExecutionContext(String jobId) {\n        this();\n        this.jobId = jobId;\n    }\n\n    public ExecutionContext(Map<String, Object> extensions) {\n        this.extensions = new ConcurrentHashMap(extensions);\n        this.temp = new ConcurrentHashMap<>();\n    }\n\n    public void putString(String key, @Nullable String value) {\n        this.put(key, value);\n    }\n\n    public void putTemp(String key, Object value){\n        this.temp.put(key, value);\n    }\n\n    public Object getTemp(String key){\n        return this.temp.get(key);\n    }\n\n    public String getString(String key) {\n        return (String) this.get(key, String.class);\n    }\n\n    public String getString(String key, String defaultString) {\n        return !this.containsKey(key) ? defaultString : this.getString(key);\n    }\n\n    public void put(String key, @Nullable Object value) {\n        if (value != null) {\n            this.extensions.put(key, value);\n        } else {\n            this.extensions.remove(key);\n        }\n    }\n\n    @Nullable\n    public Object get(String key) {\n        return this.extensions.get(key);\n    }\n\n    @Nullable\n    public <V> V get(String key, Class<V> type) {\n        Object value = this.extensions.get(key);\n        return value == null ? null : this.get(key, type, null);\n    }\n\n    @Nullable\n    public <V> V get(String key, Class<V> type, @Nullable V defaultValue) {\n        Object value = this.extensions.get(key);\n        if (value == null) {\n            return defaultValue;\n        } else if (!type.isInstance(value)) {\n            throw new ClassCastException(\n                    \"Value for key=[\" + key + \"] is not of type: [\" + type + \"], it is [(\" + value.getClass() + \")\" + value\n                            + \"]\");\n        } else {\n            return type.cast(value);\n        }\n    }\n\n    public boolean containsKey(String key) {\n        return this.extensions.containsKey(key);\n    }\n\n    @Nullable\n    public Object remove(String key) {\n        return this.extensions.remove(key);\n    }\n\n    public boolean containsValue(Object value) {\n        return this.extensions.containsValue(value);\n    }\n\n    public ExecutionContext<?> fromJsonString(String jsonString) {\n        return JsonUtil.decode(jsonString, ExecutionContext.class);\n    }\n\n    @Override\n    public String toString() {\n        return \"ExecutionContext{\" + \"param=\" + param + \", extensions=\" + extensions + '}';\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/JobBuilderFactory.java",
    "content": "package com.alibaba.cola.job;\n\nimport com.alibaba.cola.job.model.Job;\nimport com.alibaba.cola.job.model.Step;\nimport com.alibaba.cola.job.repository.JobRepository;\n\npublic class JobBuilderFactory {\n    public static JobBuilder create() {\n        return new JobBuilder();\n    }\n\n    public static class JobBuilder {\n        private final Job job = new Job();\n\n        public JobBuilder addStep(Step step) {\n            step.setJob(job);\n            job.getSteps().add(step);\n            return this;\n        }\n\n        public JobBuilder name(String name) {\n            job.setName(name);\n            return this;\n        }\n\n        public JobBuilder needRollback(boolean needRollback) {\n            job.setNeedRollback(needRollback);\n            return this;\n        }\n\n        public JobBuilder isAsync(boolean isAsync) {\n            job.setAsync(isAsync);\n            return this;\n        }\n\n        public JobBuilder jobRepository(JobRepository jobRepository) {\n            job.setJobRepository(jobRepository);\n            return this;\n        }\n\n        public Job build(String name, JobRepository jobRepository) {\n            job.setName(name);\n            job.setJobRepository(jobRepository);\n            return job;\n        }\n\n        public Job build() {\n            return job;\n        }\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/JobException.java",
    "content": "package com.alibaba.cola.job;\n\npublic class JobException extends RuntimeException{\n    public JobException(String message){\n        super(message);\n    }\n\n    public JobException(Exception e){\n        super(e);\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/JobLauncher.java",
    "content": "package com.alibaba.cola.job;\n\nimport com.alibaba.cola.job.model.ExecutionStatus;\nimport com.alibaba.cola.job.model.Job;\nimport com.alibaba.cola.job.model.JobExecution;\nimport com.alibaba.cola.job.repository.JobRepository;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.util.StringUtils;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n@Slf4j\npublic class JobLauncher {\n    private static final ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();\n\n    /**\n     * Execute job in sync mode\n     *\n     * @param job\n     * @return\n     */\n    public static JobExecution executeSync(Job job) {\n        return executeSync(job, new ExecutionContext());\n    }\n\n    /**\n     * Execute job in sync mode\n     *\n     * @param job , the job to be executed\n     * @param executionContext, the context to be used in execution runtime\n     * @return\n     */\n    public static JobExecution executeSync(Job job, ExecutionContext executionContext) {\n        // 1. 创建任务\n        JobExecution jobExecution = createJobExecution(job, executionContext);\n\n        // 2. 执行任务\n        job.execute(jobExecution);\n        return jobExecution;\n    }\n\n    /**\n     * Execute job in Async mode\n     *\n     * @param job\n     * @return jobExecutionId instantly\n     */\n    public static String executeAsync(Job job) {\n        return executeAsync(job, new ExecutionContext(), null);\n    }\n\n    public static String executeAsync(Job job, ExecutorService executorService) {\n        return executeAsync(job, new ExecutionContext(), executorService);\n    }\n\n    public static String executeAsync(Job job, ExecutionContext executionContext) {\n        return executeAsync(job, executionContext, null);\n    }\n\n    public static String executeAsync(Job job, ExecutionContext executionContext, ExecutorService executorService) {\n        // 1. 创建任务\n        final JobExecution jobExecution = createJobExecution(job, executionContext);\n\n        // 2. 执行任务\n        if (executorService != null) {\n            // 使用自定义的线程池执行任务\n            executorService.submit(() -> {\n                job.execute(jobExecution);\n            });\n        } else {\n            // 使用默认的线程池执行任务\n            DEFAULT_EXECUTOR_SERVICE.submit(() -> {\n                job.execute(jobExecution);\n            });\n        }\n\n        // 3. 立即返回任务执行id，任务异步执行\n        return jobExecution.getJobId();\n    }\n\n    private static JobExecution createJobExecution(Job job, ExecutionContext executionContext) {\n        JobRepository jobRepository = job.getJobRepository();\n        JobExecution jobExecution = null;\n        if (StringUtils.hasLength(executionContext.getJobId())) {\n            jobExecution = jobRepository.getJobExecutionByJobId(executionContext.getJobId());\n        }\n        if (jobExecution == null) {\n            // 全新的任务\n            jobExecution = new JobExecution();\n            jobExecution.setJobId(UuidGenerator.nextJobId());\n            jobExecution.setJobDef(job);\n            jobExecution.setExecutionStatus(ExecutionStatus.CREATED);\n            jobExecution.setExecutionContext(executionContext);\n            jobExecution.setBatchJobId(executionContext.getBatchJobId());\n            log.info(\"[Job] created: {}\", jobExecution);\n            jobRepository.saveJobExecution(jobExecution);\n        } else {\n            // 已经存在的任务\n            jobExecution.setJobDef(job);\n            jobExecution.setStepExecutionList(jobRepository.listStepExecutions(jobExecution.getJobId()));\n        }\n        return jobExecution;\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/UuidGenerator.java",
    "content": "package com.alibaba.cola.job;\n\nimport java.security.SecureRandom;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.Random;\n\npublic class UuidGenerator {\n    private static final char[] CHILD_ALPHABET = \"abcdefghijklmnopqrstuvwxyz\".toCharArray();\n\n    private static final char[] PARENT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\".toCharArray();\n\n    private static DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(\"yyyyMMdd'T'HHmmss-\");\n\n    private static int CUSTOM_LENGTH = 8;\n\n    /**\n     * job id, 时间+随机数，样例：20250317T141750-dgkyfjlm\n     * @return\n     */\n    public static String nextJobId() {\n        LocalDateTime currentDateTime = LocalDateTime.now();\n        String formattedDateTime = currentDateTime.format(FORMATTER);\n        return formattedDateTime + NanoIdUtils.randomNanoId(NanoIdUtils.DEFAULT_NUMBER_GENERATOR, CHILD_ALPHABET,\n                CUSTOM_LENGTH);\n    }\n\n    /**\n     * job id, 时间+随机数+jobName，样例：20250317T141750-dgkyfjlm-myJobName\n     * @return\n     */\n    public static String nextJobId(String jobName){\n        return nextJobId()+\"-\"+jobName;\n    }\n\n    /**\n     * batch job id, 时间+大写之母，样例： 20250317T141750-ADBAMLAU\n     * @return\n     */\n    public static String nextBatchJobId() {\n        LocalDateTime currentDateTime = LocalDateTime.now();\n        String formattedDateTime = currentDateTime.format(FORMATTER);\n        return formattedDateTime + NanoIdUtils.randomNanoId(NanoIdUtils.DEFAULT_NUMBER_GENERATOR, PARENT_ALPHABET,\n                CUSTOM_LENGTH);\n    }\n\n    /**\n     * 生成随机的NanoId工具内部类\n     */\n    public final class NanoIdUtils {\n        public static final SecureRandom DEFAULT_NUMBER_GENERATOR = new SecureRandom();\n        public static final char[] DEFAULT_ALPHABET = \"_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\".toCharArray();\n        public static final int DEFAULT_SIZE = 21;\n\n        private NanoIdUtils() {\n        }\n\n        public static String randomNanoId() {\n            return randomNanoId(DEFAULT_NUMBER_GENERATOR, DEFAULT_ALPHABET, 21);\n        }\n\n        public static String randomNanoId(Random random, char[] alphabet, int size) {\n            if (random == null) {\n                throw new IllegalArgumentException(\"random cannot be null.\");\n            } else if (alphabet == null) {\n                throw new IllegalArgumentException(\"alphabet cannot be null.\");\n            } else if (alphabet.length != 0 && alphabet.length < 256) {\n                if (size <= 0) {\n                    throw new IllegalArgumentException(\"size must be greater than zero.\");\n                } else {\n                    int mask = (2 << (int)Math.floor(Math.log((double)(alphabet.length - 1)) / Math.log(2.0D))) - 1;\n                    int step = (int)Math.ceil(1.6D * (double)mask * (double)size / (double)alphabet.length);\n                    StringBuilder idBuilder = new StringBuilder();\n\n                    while(true) {\n                        byte[] bytes = new byte[step];\n                        random.nextBytes(bytes);\n\n                        for(int i = 0; i < step; ++i) {\n                            int alphabetIndex = bytes[i] & mask;\n                            if (alphabetIndex < alphabet.length) {\n                                idBuilder.append(alphabet[alphabetIndex]);\n                                if (idBuilder.length() == size) {\n                                    return idBuilder.toString();\n                                }\n                            }\n                        }\n                    }\n                }\n            } else {\n                throw new IllegalArgumentException(\"alphabet must contain between 1 and 255 symbols.\");\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/config/DBAutoConfiguration.java",
    "content": "package com.alibaba.cola.job.config;\n\n\nimport jakarta.annotation.PostConstruct;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.autoconfigure.domain.EntityScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.data.jpa.repository.config.EnableJpaRepositories;\nimport org.springframework.jdbc.datasource.init.DatabasePopulator;\nimport org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;\nimport org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;\n\nimport javax.sql.DataSource;\nimport java.util.Objects;\n\n@Slf4j\n@Configuration\n@EnableJpaRepositories(basePackages = {\n        \"com.alibaba.cola.job.repository.db\"\n})\n@EntityScan(basePackages = \"com.alibaba.cola.job.model\")\n@ConditionalOnProperty(value = \"cola.job.repository-type\", havingValue = \"DB\")\npublic class DBAutoConfiguration {\n\n    @Resource\n    private DataSource dataSource;\n\n    @Resource\n    private JobProperties jobProperties;\n\n    @PostConstruct\n    public void init() {\n        log.info(\"DBAutoConfiguration jobDatabaseProperties: {}\", jobProperties.getDatabase());\n        if(jobProperties == null || jobProperties.getDatabase() == null) {\n            log.warn(\"DBAutoConfiguration jobProperties or jobProperties.getDatabase() is null, skip init sql\");\n            return;\n        }\n\n        if (jobProperties.getDatabase().getAutoDdl()) {\n            log.info(\"DBAutoConfiguration start init sql\");\n            String schema = \"schema-mysql.sql\";\n            // 可以通过ddl-location覆盖defaultSchema\n            String customizedSchema = jobProperties.getDatabase().getDdlLocation();\n            if (Objects.nonNull(customizedSchema) && !customizedSchema.isEmpty()) {\n                schema = customizedSchema;\n            }\n            DatabasePopulator databasePopulator = new ResourceDatabasePopulator(\n                    new ClassPathResource(schema));\n            DatabasePopulatorUtils.execute(databasePopulator, dataSource);\n        }\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/config/EnableColaJob.java",
    "content": "package com.alibaba.cola.job.config;\n\n\nimport org.springframework.context.annotation.Import;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Import(EnableJobConfiguration.class)\n@Documented\npublic @interface EnableColaJob {\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/config/EnableJobConfiguration.java",
    "content": "package com.alibaba.cola.job.config;\n\nimport com.alibaba.cola.job.JobException;\nimport com.alibaba.cola.job.repository.JobRepository;\nimport com.alibaba.cola.job.repository.JsonUtil;\nimport com.alibaba.cola.job.repository.RepositoryType;\nimport com.alibaba.cola.job.repository.db.DataBaseJobRepository;\nimport com.alibaba.cola.job.repository.memory.MemoryJobRepository;\nimport com.alibaba.cola.job.repository.redis.RedisJobRepository;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Import;\n\n@Slf4j\n@Import( {JobProperties.class, DBAutoConfiguration.class, RedisConfig.class, JsonUtil.class})\n@ComponentScan(\"com.alibaba.cola.job\")\npublic class EnableJobConfiguration {\n\n    @Resource\n    private JobProperties jobProperties;\n\n    @Bean\n    public JobRepository jobRepository() {\n        if (jobProperties.getRepositoryType() == null) {\n            jobProperties.setRepositoryType(RepositoryType.MEMORY);\n        }\n        JobRepository jobRepository = switch (jobProperties.getRepositoryType()) {\n            case DB -> new DataBaseJobRepository();\n            case REDIS -> new RedisJobRepository();\n            case MEMORY -> new MemoryJobRepository();\n            default -> throw new JobException(\"not support repositoryType: \" + jobProperties.getRepositoryType());\n        };\n        log.info(\"[cola-job]create JobRepository: \" + jobRepository.getClass().getSimpleName());\n        return jobRepository;\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/config/JobProperties.java",
    "content": "package com.alibaba.cola.job.config;\n\nimport com.alibaba.cola.job.repository.RepositoryType;\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Configuration;\n\n@Data\n@Configuration\n@ConfigurationProperties(prefix = \"cola.job\")\npublic class JobProperties {\n    private RepositoryType repositoryType;\n    private DatabaseProperties database;\n\n    // database配置，数据库配置会自动映射到 cola.job.database 路径下\n    @Data\n    public static class DatabaseProperties {\n        private Boolean autoDdl;\n        private String ddlLocation;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/config/RedisConfig.java",
    "content": "package com.alibaba.cola.job.config;\n\nimport com.alibaba.cola.job.repository.JsonUtil;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.boot.autoconfigure.AutoConfigureAfter;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;\nimport org.springframework.data.redis.serializer.StringRedisSerializer;\n\n@Configuration\n@AutoConfigureAfter(EnableJobConfiguration.class)\npublic class RedisConfig {\n    @Bean(\"jobRedisTemplate\")\n    public RedisTemplate<String, Object> jobRedisTemplate(RedisConnectionFactory redisConnectionFactory) {\n        return buildTemplate(redisConnectionFactory, JsonUtil.ObjectMapperFactory.getObjectMapper());\n    }\n\n    @Bean(\"stepRedisTemplate\")\n    public RedisTemplate<String, Object> stepRedisTemplate(RedisConnectionFactory redisConnectionFactory) {\n        return buildTemplate(redisConnectionFactory, JsonUtil.ObjectMapperFactory.getObjectMapper());\n    }\n\n    @Bean(\"batchJobRedisTemplate\")\n    public RedisTemplate<String, Object> batchJobRedisTemplate(RedisConnectionFactory redisConnectionFactory) {\n        return buildTemplate(redisConnectionFactory, JsonUtil.ObjectMapperFactory.getObjectMapper());\n    }\n\n    private RedisTemplate<String, Object> buildTemplate(RedisConnectionFactory redisConnectionFactory,\n                                                        ObjectMapper objectMapper) {\n        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();\n        redisTemplate.setConnectionFactory(redisConnectionFactory);\n        redisTemplate.setKeySerializer(new StringRedisSerializer());\n        redisTemplate.setHashKeySerializer(new StringRedisSerializer());\n\n        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(\n                objectMapper, Object.class);\n        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);\n        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);\n        return redisTemplate;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/model/AbstractStep.java",
    "content": "package com.alibaba.cola.job.model;\n\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.time.LocalDateTime;\n\n@Data\n@Slf4j\npublic abstract class AbstractStep implements Step {\n\n    private Job job;\n\n    @Override\n    public void setJob(Job job) {\n        this.job = job;\n    }\n\n    @Override\n    public Job getJob() {\n        return this.job;\n    }\n\n    @Override\n    public void execute(StepExecution stepExecution) {\n        try {\n            logInfo(stepExecution, \"start\");\n            this.doExecute(stepExecution);\n            stepExecution.setExecutionStatus(ExecutionStatus.COMPLETED);\n        } catch (Exception e) {\n            log.error(\"[job - {}][Step - {}] need rollback: {},execute error: {}\",\n                    stepExecution.getJobExecution().getJobId(), stepExecution.getStepName(),\n                    this.needRollBack(stepExecution), e.getMessage());\n            stepExecution.setMessage(\"[execute error]: \" + e.getMessage() + \"; \");\n            if (this.needRollBack(stepExecution)) {\n                this.internalRollback(stepExecution);\n            } else {\n                stepExecution.setExecutionStatus(ExecutionStatus.FAILED);\n            }\n            throw e;\n        } finally {\n            updateStepExecution(stepExecution);\n        }\n    }\n\n    @Override\n    public void rollback(StepExecution stepExecution) {\n        try {\n            internalRollback(stepExecution);\n        } finally {\n            updateStepExecution(stepExecution);\n        }\n    }\n\n    private void updateStepExecution(StepExecution stepExecution) {\n        logInfo(stepExecution, \"end, status is [\" + stepExecution.getExecutionStatus()+\"]\");\n        stepExecution.setEndTime(LocalDateTime.now());\n        stepExecution.getRepository().updateStepExecution(stepExecution);\n    }\n\n    private void internalRollback(StepExecution stepExecution) {\n        try {\n            logInfo(stepExecution, \"rollback start\");\n            stepExecution.setExecutionStatus(ExecutionStatus.ROLLBACK_STARTED);\n            this.doRollback(stepExecution);\n            stepExecution.setExecutionStatus(ExecutionStatus.ROLLBACK_COMPLETED);\n            logInfo(stepExecution, \"rollback end\");\n        } catch (Exception e) {\n            String errMsg = String.format(\"[Job - %s][Step - %s]Rollback with error: %s\",\n                    stepExecution.getJobExecution().getJobId(), stepExecution.getStepName(), e.getMessage());\n            log.error(errMsg, e);\n            stepExecution.setMessage(stepExecution.getMessage() + \"[rollback error]: \" + e.getMessage());\n            stepExecution.setExecutionStatus(ExecutionStatus.ROLLBACK_FAILED);\n            throw e;\n        }\n    }\n\n    public abstract void doExecute(StepExecution stepExecution);\n\n    public void doRollback(StepExecution stepExecution) {\n        log.info(\"[job - {}][Step - {}] The rollback content is empty.\", stepExecution.getJobExecution().getJobId(),\n                stepExecution.getStepName());\n    }\n\n    @Override\n    public boolean needRollBack(StepExecution stepExecution) {\n        return false;\n    }\n\n    private void logInfo(StepExecution stepExecution, String message) {\n        log.info(\"[Job - {}][Step - {}] {}\", stepExecution.getJobExecution().getJobId(), stepExecution.getStepName(),\n                message);\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/model/BatchJob.java",
    "content": "package com.alibaba.cola.job.model;\n\nimport com.alibaba.cola.job.repository.JobRepository;\nimport lombok.Getter;\nimport lombok.Setter;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\n\n/**\n * 一个批量任务，包含了多个JobInstance\n */\npublic class BatchJob {\n    @Getter\n    private final List<JobInstance> jobInstances;\n\n    @Getter\n    private JobRepository jobRepository;\n\n    @Getter\n    @Setter\n    private String jobType;\n\n    @Getter\n    private ExecutorService executorService;\n\n    public BatchJob() {\n        jobInstances = new ArrayList<>();\n    }\n\n    public BatchJob jobRepository(JobRepository jobRepository) {\n        this.jobRepository = jobRepository;\n        return this;\n    }\n\n    public BatchJob executorService(ExecutorService executorService) {\n        this.executorService = executorService;\n        return this;\n    }\n\n    public BatchJob add(JobInstance jobInstance) {\n        jobInstances.add(jobInstance);\n        return this;\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/model/BatchJobExecution.java",
    "content": "package com.alibaba.cola.job.model;\n\n\nimport com.alibaba.cola.job.repository.JsonUtil;\nimport com.fasterxml.jackson.core.type.TypeReference;\n\nimport jakarta.persistence.Access;\nimport jakarta.persistence.AccessType;\nimport jakarta.persistence.AttributeConverter;\nimport jakarta.persistence.Column;\nimport jakarta.persistence.Convert;\nimport jakarta.persistence.Entity;\nimport jakarta.persistence.EnumType;\nimport jakarta.persistence.Enumerated;\nimport jakarta.persistence.Id;\nimport jakarta.persistence.Temporal;\nimport jakarta.persistence.TemporalType;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Data\n@Entity\n@Access(AccessType.FIELD)\npublic class BatchJobExecution {\n    @Id\n    @Column(name = \"batch_job_id\")\n    private String batchJobId;\n\n    @Column(name = \"job_type\")\n    private String jobType;\n\n    @Column(name = \"execution_status\")\n    @Enumerated(EnumType.STRING)\n    private ExecutionStatus status;\n\n    @Column(name = \"job_execution_results\")\n    @Convert(converter = ResultsConverter.class)\n    // key: jobExecutionId, value: 执行的结果状态\n    private Map<String, ExecutionStatus> jobExecutionResults = new HashMap<>();\n\n    @Column(name = \"start_time\")\n    @Temporal(TemporalType.TIMESTAMP)\n    private LocalDateTime startTime;\n\n    @Column(name = \"end_time\")\n    @Temporal(TemporalType.TIMESTAMP)\n    private LocalDateTime endTime;\n\n    public BatchJobExecution() {\n    }\n\n    public BatchJobExecution(String batchJobId) {\n        this.batchJobId = batchJobId;\n    }\n\n    public void addJobExecution(String jobExecutionId) {\n        jobExecutionResults.put(jobExecutionId, ExecutionStatus.STARTED);\n    }\n\n    public void put(String jobExecutionId, ExecutionStatus executionStatus) {\n        jobExecutionResults.put(jobExecutionId, executionStatus);\n    }\n\n    public boolean isCompleted() {\n        if (status == ExecutionStatus.COMPLETED) {\n            return true;\n        }\n        return isAllJobsCompleted();\n    }\n\n    public boolean isBatchJobCompleted() {\n        return status == ExecutionStatus.COMPLETED;\n    }\n\n    public boolean isChildrenJobsCompleted() {\n        for (ExecutionStatus jobStatus : jobExecutionResults.values()) {\n            if (jobStatus != ExecutionStatus.COMPLETED) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public boolean isAllJobsCompleted() {\n        for (ExecutionStatus jobStatus : jobExecutionResults.values()) {\n            if (jobStatus != ExecutionStatus.COMPLETED) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * 检查batchJob中是否所有的job都已经达到终态\n     * 终态的定义是：Completed，Failed or Rollback\n     * @return\n     */\n    public boolean isTerminated(){\n        for (ExecutionStatus jobStatus : jobExecutionResults.values()) {\n            if (!ExecutionStatus.isTerminated(jobStatus)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public boolean isFailed() {\n        for (ExecutionStatus jobStatus : jobExecutionResults.values()) {\n            if (jobStatus == ExecutionStatus.FAILED) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n}\n\nclass ResultsConverter implements AttributeConverter<Map<String, ExecutionStatus>, String> {\n    @Override\n    public String convertToDatabaseColumn(Map attribute) {\n        return JsonUtil.encode(attribute);\n    }\n\n    @Override\n    public Map<String, ExecutionStatus> convertToEntityAttribute(String dbData) {\n        return JsonUtil.decode(dbData, new TypeReference<>() { });\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/model/ExecutionStatus.java",
    "content": "package com.alibaba.cola.job.model;\n\nimport com.alibaba.cola.job.JobException;\nimport com.fasterxml.jackson.annotation.JsonCreator;\n\nimport java.util.List;\n\npublic enum ExecutionStatus {\n    CREATED,\n    STARTED,\n    COMPLETED,\n    FAILED,\n    RUNNING,\n    ROLLBACK_STARTED,\n    ROLLBACK_COMPLETED,\n    ROLLBACK_FAILED,\n    UNKNOWN;\n\n    public final static List<ExecutionStatus> ROLLBACK_STATUS = List.of(ROLLBACK_STARTED, ROLLBACK_FAILED,\n            ROLLBACK_COMPLETED);\n\n    @JsonCreator\n    public static ExecutionStatus fromValue(String value) {\n        for (ExecutionStatus s : ExecutionStatus.values()) {\n            if (s.name().equals(value)) {\n                return s;\n            }\n        }\n        throw new JobException(\"Unexpected value '\" + value + \"'\");\n    }\n\n    public static boolean isRollback(ExecutionStatus executionStatus) {\n        return executionStatus != null && ROLLBACK_STATUS.contains(executionStatus);\n    }\n\n    public static boolean isTerminated(ExecutionStatus executionStatus) {\n        return executionStatus == ExecutionStatus.COMPLETED\n                || executionStatus == ExecutionStatus.FAILED\n                || isRollback(executionStatus);\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/model/Job.java",
    "content": "package com.alibaba.cola.job.model;\n\n\nimport com.alibaba.cola.job.repository.JobRepository;\n\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * 任务的定义，主要定义了任务所包含的steps，以及相关metadata，比如任务形态（同步，异步），是否需要回滚。\n */\n@Slf4j\n@Data\npublic class Job {\n\n    private String name;\n\n    private List<Step> steps = new ArrayList<>();\n\n    private JobRepository jobRepository;\n\n    // 任务是否需要回滚\n    private boolean needRollback;\n\n    // 任务是否是异步任务，主要影响任务的状态设置\n    private boolean isAsync;\n\n    public void execute(JobExecution jobExecution) {\n        if (ExecutionStatus.isRollback(jobExecution.getExecutionStatus())) {\n            rollback(jobExecution);\n        } else {\n            run(jobExecution);\n        }\n    }\n\n    private void run(JobExecution jobExecution) {\n        jobExecution.setExecutionStatus(ExecutionStatus.STARTED);\n        jobExecution.setStartTime(LocalDateTime.now());\n        log.info(\"[Job - {}] started: {}\", jobExecution.getJobId(), jobExecution);\n        this.jobRepository.updateJobExecution(jobExecution);\n        try {\n            for (Step step : this.getSteps()) {\n                runStep(step, jobExecution);\n                jobExecution.getCompletedSteps().add(step);\n            }\n            if (isAsync) {\n                // 对于异步任务，触发完所有的steps，状态仍然是running\n                jobExecution.setExecutionStatus(ExecutionStatus.RUNNING);\n            } else {\n                // 对于同步任务，执行完所有的steps，不出问题的话，任务就Completed了\n                jobExecution.setEndTime(LocalDateTime.now());\n                jobExecution.setExecutionStatus(ExecutionStatus.COMPLETED);\n            }\n\n        } catch (Exception e) {\n            if (this.isNeedRollback()) {\n                onFailToRollback(e, jobExecution);\n            } else {\n                onException(e, jobExecution, ExecutionStatus.FAILED);\n            }\n        } finally {\n            onFinally(jobExecution);\n        }\n    }\n\n    private void onFailToRollback(Exception e, JobExecution jobExecution) {\n        log.error(\"[Job - {}] execute error, need to rollback, error: {}\", jobExecution.getJobId(), e.getMessage(), e);\n        jobExecution.setExecutionStatus(ExecutionStatus.ROLLBACK_STARTED);\n        try {\n            List<Step> rollbackSteps = jobExecution.getRollbackSteps();\n            log.info(\"[Job - {}] start rollback: {}\", jobExecution.getJobId(),\n                    rollbackSteps.stream().map(s -> s.getClass().getSimpleName()).collect(Collectors.toList()));\n            rollbackSteps.forEach(s -> rollbackStep(s, jobExecution));\n        } catch (Exception ee) {\n            log.error(\"[Job - {}] rollback error: \", jobExecution.getJobId(), ee);\n            jobExecution.setExecutionStatus(ExecutionStatus.ROLLBACK_FAILED);\n            return;\n        }\n        jobExecution.setExecutionStatus(ExecutionStatus.ROLLBACK_COMPLETED);\n    }\n\n    private void rollbackStep(Step step, JobExecution jobExecution) {\n        StepExecution stepExecution = this.jobRepository.getStepExecution(jobExecution.getJobId(),\n                step.getClass().getSimpleName());\n\n        if (stepExecution.isRollbacked()) {\n            log.info(\"[Job - {}][Step - {}] rollback skip\", jobExecution.getJobId(), stepExecution.getStepName());\n        } else {\n            if (step.needRollBack(stepExecution)) {\n                // 因为stepExecution是持久层取出的，所以要重新设置step和jobExecution\n                stepExecution.setStep(step);\n                stepExecution.setJobExecution(jobExecution);\n                step.rollback(stepExecution);\n            } else {\n                log.info(\"[Job - {}][Step - {}] no need rollback\", jobExecution.getJobId(),\n                        stepExecution.getStepName());\n            }\n        }\n    }\n\n    private void runStep(Step step, JobExecution jobExecution) {\n        StepExecution stepExecution = this.jobRepository.getStepExecution(jobExecution.getJobId(),\n                step.getClass().getSimpleName());\n        // 全新的step\n        if (stepExecution == null) {\n            stepExecution = new StepExecution(step);\n            stepExecution.setJobExecution(jobExecution);\n            stepExecution.setExecutionStatus(ExecutionStatus.STARTED);\n            stepExecution.setStartTime(LocalDateTime.now());\n            this.jobRepository.saveStepExecution(stepExecution);\n            step.execute(stepExecution);\n            return;\n        }\n        // step已经执行成功，跳过\n        if (stepExecution.isCompleted()) {\n            log.info(\"[Job - {}][Step - {}] skip\", jobExecution.getJobId(), stepExecution.getStepName());\n        } else {\n            // 因为stepExecution是持久层取出的，所以要重新设置step和jobExecution\n            stepExecution.setStep(step);\n            stepExecution.setJobExecution(jobExecution);\n            stepExecution.setExecutionStatus(ExecutionStatus.STARTED);\n            this.jobRepository.updateStepExecution(stepExecution);\n            step.execute(stepExecution);\n        }\n    }\n\n    private void rollback(JobExecution jobExecution) {\n        jobExecution.setExecutionStatus(ExecutionStatus.ROLLBACK_STARTED);\n        jobExecution.setStartTime(LocalDateTime.now());\n        log.info(\"[Job - {}] rollback started: {}\", jobExecution.getJobId(), jobExecution);\n        this.jobRepository.updateJobExecution(jobExecution);\n        try {\n            for (Step step : getReverseSteps()) {\n                StepExecution stepExecution = this.jobRepository.getStepExecution(jobExecution.getJobId(),\n                        step.getClass().getSimpleName());\n                if (stepExecution == null) {\n                    continue;\n                }\n                // step已经执行成功，跳过\n                if (stepExecution.isRollbacked()) {\n                    log.info(\"[Job - {}][Step - {}] rollback skip\", jobExecution.getJobId(),\n                            stepExecution.getStepName());\n                } else {\n                    stepExecution.setStep(step);\n                    stepExecution.setJobExecution(jobExecution);\n                    stepExecution.setExecutionStatus(ExecutionStatus.ROLLBACK_STARTED);\n                    this.jobRepository.updateStepExecution(stepExecution);\n                    step.rollback(stepExecution);\n                }\n            }\n            jobExecution.setExecutionStatus(ExecutionStatus.ROLLBACK_COMPLETED);\n        } catch (Exception e) {\n            onException(e, jobExecution, ExecutionStatus.ROLLBACK_FAILED);\n        } finally {\n            onFinally(jobExecution);\n        }\n    }\n\n    public List<Step> getReverseSteps() {\n        List<Step> reverseSteps = new ArrayList<>(this.steps);\n        Collections.reverse(reverseSteps);\n        return reverseSteps;\n    }\n\n    private void onException(Exception e, JobExecution jobExecution, ExecutionStatus status) {\n        log.error(\"[Job - {}] execute error: {}\", jobExecution.getJobId(), e.getMessage(), e);\n        jobExecution.setMessage(e.getMessage());\n        jobExecution.setEndTime(LocalDateTime.now());\n        jobExecution.setExecutionStatus(status);\n    }\n\n    private void onFinally(JobExecution jobExecution) {\n        jobExecution.setUpdateTime(LocalDateTime.now());\n        log.info(\"[Job - {}] end, status is [{}]\", jobExecution.getJobId(), jobExecution.getExecutionStatus());\n        this.jobRepository.updateJobExecution(jobExecution);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (!(o instanceof Job)) {\n            return false;\n        }\n\n        Job job = (Job) o;\n\n        if (isNeedRollback() != job.isNeedRollback()) {\n            return false;\n        }\n        return getName() != null ? getName().equals(job.getName()) : job.getName() == null;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = getName() != null ? getName().hashCode() : 0;\n        result = 31 * result + (isNeedRollback() ? 1 : 0);\n        return result;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/model/JobExecution.java",
    "content": "package com.alibaba.cola.job.model;\n\nimport com.alibaba.cola.job.ExecutionContext;\nimport com.alibaba.cola.job.repository.JsonUtil;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.core.type.TypeReference;\n\nimport jakarta.persistence.Access;\nimport jakarta.persistence.AccessType;\nimport jakarta.persistence.AttributeConverter;\nimport jakarta.persistence.Column;\nimport jakarta.persistence.Convert;\nimport jakarta.persistence.Entity;\nimport jakarta.persistence.EnumType;\nimport jakarta.persistence.Enumerated;\nimport jakarta.persistence.Id;\nimport jakarta.persistence.Temporal;\nimport jakarta.persistence.TemporalType;\nimport jakarta.persistence.Transient;\nimport lombok.Data;\n\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n@Data\n@Entity\n@Access(AccessType.FIELD)\npublic class JobExecution {\n    @Id\n    @Column(name = \"job_id\")\n    private String jobId;\n\n    // 用来关联批量任务的batchJobId\n    @Column(name = \"batch_job_id\")\n    private String batchJobId;\n\n    @Column(name = \"start_time\")\n    @Temporal(TemporalType.TIMESTAMP)\n    private LocalDateTime startTime;\n\n    @Column(name = \"update_time\")\n    @Temporal(TemporalType.TIMESTAMP)\n    private LocalDateTime updateTime;\n\n    @Column(name = \"end_time\")\n    @Temporal(TemporalType.TIMESTAMP)\n    private LocalDateTime endTime;\n\n    @Column(name = \"execution_context\")\n    @Convert(converter = ContextConverter.class)\n    private ExecutionContext executionContext;\n\n    @Column(name = \"execution_status\")\n    @Enumerated(EnumType.STRING)\n    private ExecutionStatus executionStatus;\n\n    @Column(name = \"message\")\n    private String message;\n\n    @JsonIgnore\n    @Transient\n    private List<StepExecution> stepExecutions;\n\n    // JobExecution所从属的job的定义definition\n    @JsonIgnore\n    @Transient\n    private Job jobDef;\n\n    // 已经执行的任务，用于回滚\n    @JsonIgnore\n    @Transient\n    private List<Step> completedSteps = new ArrayList<>();\n\n    public void setStepExecutionList(List<StepExecution> stepExecutions) {\n        if (stepExecutions == null) {\n            return;\n        }\n        this.stepExecutions = stepExecutions;\n        for (StepExecution stepExecution : this.stepExecutions) {\n            stepExecution.setJobExecution(this);\n        }\n    }\n\n    public boolean isCompleted() {\n        return ExecutionStatus.COMPLETED == this.executionStatus;\n    }\n\n    public boolean isFailOrRollback() {\n        return ExecutionStatus.FAILED == this.executionStatus || ExecutionStatus.isRollback(this.executionStatus);\n    }\n\n    @Override\n    public String toString() {\n        return \"JobExecution{\" + \"jobId='\" + jobId + '\\'' + \", startTime=\" + startTime + \", endTime=\" + endTime\n                + \", executionStatus=\" + executionStatus + '}';\n    }\n\n    @JsonIgnore\n    public List<Step> getRollbackSteps() {\n        List<Step> rollbackSteps = new ArrayList<>(completedSteps);\n        Collections.reverse(rollbackSteps);\n        return rollbackSteps;\n    }\n}\n\nclass ContextConverter implements AttributeConverter<ExecutionContext, String> {\n    @Override\n    public String convertToDatabaseColumn(ExecutionContext attribute) {\n        return JsonUtil.encode(attribute);\n    }\n\n    @Override\n    public ExecutionContext convertToEntityAttribute(String dbData) {\n        return JsonUtil.decode(dbData, new TypeReference<>() { });\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/model/JobInstance.java",
    "content": "package com.alibaba.cola.job.model;\n\n\nimport com.alibaba.cola.job.ExecutionContext;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\n/**\n * Job实例：包含了Job的定义，以及运行job的runtime信息，也就是ExecutionContext\n * 一个BatchJob会包含多个JobInstance，是为了BatchJob创建的模型。\n */\n@Data\n@AllArgsConstructor\npublic class JobInstance {\n    private Job job;\n    private ExecutionContext executionContext;\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/model/Step.java",
    "content": "package com.alibaba.cola.job.model;\n\npublic interface Step {\n\n    void execute(StepExecution stepExecution);\n\n    void rollback(StepExecution stepExecution);\n\n    boolean needRollBack(StepExecution stepExecution);\n\n    void setJob(Job job);\n\n    Job getJob();\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/model/StepExecution.java",
    "content": "package com.alibaba.cola.job.model;\n\n\nimport com.alibaba.cola.job.ExecutionContext;\nimport com.alibaba.cola.job.repository.JobRepository;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\n\nimport jakarta.persistence.Access;\nimport jakarta.persistence.AccessType;\nimport jakarta.persistence.Column;\nimport jakarta.persistence.Convert;\nimport jakarta.persistence.Entity;\nimport jakarta.persistence.EnumType;\nimport jakarta.persistence.Enumerated;\nimport jakarta.persistence.Id;\nimport jakarta.persistence.Temporal;\nimport jakarta.persistence.TemporalType;\nimport jakarta.persistence.Transient;\nimport lombok.Data;\n\nimport org.springframework.util.Assert;\n\nimport java.time.LocalDateTime;\n\n@Data\n@Entity\n@Access(AccessType.FIELD)\npublic class StepExecution {\n    @Id\n    @Column(name = \"step_id\")\n    // stepId = jobId + \"-\" +stepName\n    private String stepId;\n\n    // StepExecution的Step定义\n    @JsonIgnore\n    @Transient\n    private Step step;\n\n    // 该stepExecution所归属的jobExecution\n    @JsonIgnore\n    @Transient\n    private JobExecution jobExecution;\n\n    @Column(name = \"job_id\")\n    private String jobId;\n\n    @Column(name = \"step_name\")\n    private String stepName;\n\n    @Column(name = \"start_time\")\n    @Temporal(TemporalType.TIMESTAMP)\n    private LocalDateTime startTime;\n\n    @Column(name = \"end_time\")\n    @Temporal(TemporalType.TIMESTAMP)\n    private LocalDateTime endTime;\n\n    @Column(name = \"execution_status\")\n    @Enumerated(EnumType.STRING)\n    private ExecutionStatus executionStatus;\n\n    @Column(name = \"execution_context\")\n    @Convert(converter = ContextConverter.class)\n    private ExecutionContext executionContext;\n\n    @Column(name = \"message\")\n    private String message;\n\n    public boolean isCompleted() {\n        return this.executionStatus == ExecutionStatus.COMPLETED;\n    }\n    public boolean isRollbacked() {\n        return this.executionStatus == ExecutionStatus.ROLLBACK_COMPLETED;\n    }\n\n    public StepExecution(){\n    }\n\n    public StepExecution(Step step) {\n        this.step = step;\n        this.stepName = step.getClass().getSimpleName();\n    }\n\n    public void setJobExecution(JobExecution jobExecution) {\n        Assert.notNull(jobExecution, \"jobExecution cannot be null\");\n        this.jobExecution = jobExecution;\n        this.jobId = jobExecution.getJobId();\n        this.stepId = this.jobId + \"-\" + this.stepName;\n    }\n\n    public ExecutionContext getExecutionContext() {\n        if (executionContext == null) {\n            executionContext = this.jobExecution.getExecutionContext();\n        }\n        return executionContext;\n    }\n\n    @JsonIgnore\n    public JobRepository getRepository() {\n        Assert.notNull(jobExecution, \"jobExecution cannot be null\");\n        Assert.notNull(jobExecution.getJobDef(), \"job must be attached to jobExecution\");\n        return this.jobExecution.getJobDef().getJobRepository();\n    }\n\n    @Override\n    public String toString() {\n        return \"StepExecution{\" + \"stepExecutionId='\" + stepId + '\\'' + \", executionStatus=\" + executionStatus + '}';\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (!(o instanceof StepExecution)) {\n            return false;\n        }\n        StepExecution that = (StepExecution) o;\n        return stepId != null\n                ? stepId.equals(that.getStepId())\n                : that.getStepId() == null;\n    }\n\n    @Override\n    public int hashCode() {\n        return getJobExecution() != null ? stepId.hashCode() : 0;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/repository/AbstractJobRepository.java",
    "content": "package com.alibaba.cola.job.repository;\n\nimport com.alibaba.cola.job.model.BatchJobExecution;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\npublic abstract class AbstractJobRepository implements JobRepository {\n    @Override\n    public List<BatchJobExecution> findNotCompletedBatchJobsOlderThan(LocalDateTime thresholdTime) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/repository/JobRepository.java",
    "content": "package com.alibaba.cola.job.repository;\n\n\nimport com.alibaba.cola.job.model.BatchJobExecution;\nimport com.alibaba.cola.job.model.JobExecution;\nimport com.alibaba.cola.job.model.StepExecution;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\npublic interface JobRepository {\n    BatchJobExecution saveBatchJobExecution(BatchJobExecution batchJobExecution);\n\n    void updateBatchJobExecution(BatchJobExecution batchJobExecution);\n\n    BatchJobExecution getBatchJobExecutionByBatchJobId(String batchJobId);\n\n    JobExecution saveJobExecution(JobExecution jobExecution);\n\n    void updateJobExecution(JobExecution jobExecution);\n\n    JobExecution getJobExecutionByJobId(String jobId);\n\n    void saveStepExecution(StepExecution stepExecution);\n\n    void updateStepExecution(StepExecution stepExecution);\n\n    StepExecution getStepExecution(String jobExecutionId, String stepName);\n\n    List<StepExecution> listStepExecutions(String jobExecutionId);\n\n    List<BatchJobExecution> findNotCompletedBatchJobsOlderThan(LocalDateTime thresholdTime);\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/repository/JsonUtil.java",
    "content": "package com.alibaba.cola.job.repository;\n\nimport com.alibaba.cola.job.JobException;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.PropertyAccessor;\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\nimport org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;\nimport com.fasterxml.jackson.core.type.TypeReference;\n\nimport java.util.concurrent.Callable;\n\npublic final class JsonUtil {\n    private static ObjectMapper OBJECT_MAPPER = ObjectMapperFactory.getObjectMapper();\n\n    public static String encode(Object object) {\n        return invoke(() -> OBJECT_MAPPER.writeValueAsString(object));\n    }\n\n    public static <T> T decode(String source, Class<T> valueType) {\n        return invoke(() -> OBJECT_MAPPER.readValue(source, valueType));\n    }\n\n    public static <T> T decode(String src, TypeReference<T> valueTypeRef) {\n        return invoke(() -> OBJECT_MAPPER.readValue(src, valueTypeRef));\n    }\n\n    private static <T> T invoke(Callable<T> callable) {\n        try {\n            return callable.call();\n        } catch (Exception e) {\n            throw new JobException(e);\n        }\n    }\n\n    public  class ObjectMapperFactory {\n\n        public static ObjectMapper OBJECT_MAPPER;\n\n        static {\n            OBJECT_MAPPER = Jackson2ObjectMapperBuilder.json().build();\n            OBJECT_MAPPER.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);\n            OBJECT_MAPPER.findAndRegisterModules();\n        }\n\n        public static ObjectMapper getObjectMapper() {\n            return OBJECT_MAPPER;\n        }\n    }\n}\n\n\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/repository/RepositoryType.java",
    "content": "package com.alibaba.cola.job.repository;\n\npublic enum RepositoryType {\n    REDIS,\n    DB,\n    MEMORY\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/repository/db/BatchJobExecutionRepository.java",
    "content": "package com.alibaba.cola.job.repository.db;\n\n\nimport com.alibaba.cola.job.model.BatchJobExecution;\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.springframework.data.jpa.repository.Query;\nimport org.springframework.data.repository.query.Param;\nimport org.springframework.stereotype.Repository;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\n@Repository\npublic interface BatchJobExecutionRepository extends JpaRepository<BatchJobExecution, String> {\n    BatchJobExecution getByBatchJobId(String batchJobId);\n\n    @Query(\"SELECT b FROM BatchJobExecution b \" +\n            \"WHERE b.status <> 'COMPLETED' \" +\n            \"AND b.startTime < :thresholdTime\")\n    List<BatchJobExecution> findNotCompletedBatchJobsOlderThan(\n            @Param(\"thresholdTime\") LocalDateTime thresholdTime);\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/repository/db/DataBaseJobRepository.java",
    "content": "package com.alibaba.cola.job.repository.db;\n\n\nimport com.alibaba.cola.job.model.BatchJobExecution;\nimport com.alibaba.cola.job.model.JobExecution;\nimport com.alibaba.cola.job.model.StepExecution;\nimport com.alibaba.cola.job.repository.AbstractJobRepository;\nimport jakarta.annotation.Resource;\n\nimport java.time.LocalDateTime;\nimport java.util.List;\n\npublic class DataBaseJobRepository extends AbstractJobRepository {\n\n    @Resource\n    private BatchJobExecutionRepository batchJobExecutionRepository;\n\n    @Resource\n    private JobExecutionRepository jobExecutionRepository;\n\n    @Resource\n    private StepExecutionRepository stepExecutionRepository;\n\n    @Override\n    public JobExecution getJobExecutionByJobId(String jobId) {\n        return jobExecutionRepository.getByJobId(jobId);\n    }\n\n    @Override\n    public BatchJobExecution saveBatchJobExecution(BatchJobExecution batchJobExecution) {\n        return batchJobExecutionRepository.save(batchJobExecution);\n    }\n\n    @Override\n    public void updateBatchJobExecution(BatchJobExecution batchJobExecution) {\n        batchJobExecutionRepository.save(batchJobExecution);\n    }\n\n    @Override\n    public BatchJobExecution getBatchJobExecutionByBatchJobId(String batchJobId) {\n        return batchJobExecutionRepository.getByBatchJobId(batchJobId);\n    }\n\n    @Override\n    public JobExecution saveJobExecution(JobExecution jobExecution) {\n        return jobExecutionRepository.save(jobExecution);\n    }\n\n    @Override\n    public void updateJobExecution(JobExecution jobExecution) {\n        jobExecutionRepository.save(jobExecution);\n    }\n\n    @Override\n    public void saveStepExecution(StepExecution stepExecution) {\n        stepExecutionRepository.save(stepExecution);\n    }\n\n    @Override\n    public void updateStepExecution(StepExecution stepExecution) {\n        stepExecutionRepository.save(stepExecution);\n    }\n\n    @Override\n    public StepExecution getStepExecution(String jobId, String stepName) {\n        return stepExecutionRepository.getByJobIdAndStepName(jobId, stepName);\n    }\n\n    @Override\n    public List<StepExecution> listStepExecutions(String jobId) {\n        return stepExecutionRepository.findByJobId(jobId);\n    }\n\n    @Override\n    public List<BatchJobExecution> findNotCompletedBatchJobsOlderThan(LocalDateTime thresholdTime) {\n        return batchJobExecutionRepository.findNotCompletedBatchJobsOlderThan(thresholdTime);\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/repository/db/JobExecutionRepository.java",
    "content": "package com.alibaba.cola.job.repository.db;\n\n\nimport com.alibaba.cola.job.model.JobExecution;\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface JobExecutionRepository extends JpaRepository<JobExecution, String> {\n    JobExecution getByJobId(String jobId);\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/repository/db/StepExecutionRepository.java",
    "content": "package com.alibaba.cola.job.repository.db;\n\nimport com.alibaba.cola.job.model.StepExecution;\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.List;\n\n@Repository\npublic interface StepExecutionRepository extends JpaRepository<StepExecution, String> {\n    StepExecution getByStepId(String stepId);\n    StepExecution getByJobIdAndStepName(String jobId, String stepName);\n    List<StepExecution> findByJobId(String jobId);\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/repository/memory/MemoryJobRepository.java",
    "content": "package com.alibaba.cola.job.repository.memory;\n\n\nimport com.alibaba.cola.job.JobException;\nimport com.alibaba.cola.job.repository.AbstractJobRepository;\nimport com.alibaba.cola.job.model.BatchJobExecution;\nimport com.alibaba.cola.job.model.JobExecution;\nimport com.alibaba.cola.job.model.StepExecution;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.util.Assert;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 内存任务仓储，没有落盘持久化，适合Demo和一次性的任务\n */\npublic class MemoryJobRepository extends AbstractJobRepository {\n\n    // key是batchJobId\n    private Map<String, BatchJobExecution> batchJobMap = new ConcurrentHashMap<>();\n\n    // key是jobExecutionId\n    private Map<String, JobExecution> jobMap = new ConcurrentHashMap<>();\n\n    // key是jobExecutionId+stepName\n    private Map<String, StepExecution> stepMap = new ConcurrentHashMap<>();\n\n    // key是jobExecutionId\n    private Map<String, Set<StepExecution>> stepSetMap = new ConcurrentHashMap<>();\n\n    @Override\n    public JobExecution getJobExecutionByJobId(String jobId) {\n        return jobMap.get(jobId);\n    }\n\n    @Override\n    public BatchJobExecution saveBatchJobExecution(BatchJobExecution batchJobExecution) {\n        batchJobMap.put(batchJobExecution.getBatchJobId(), batchJobExecution);\n        return batchJobExecution;\n    }\n\n    @Override\n    public void updateBatchJobExecution(BatchJobExecution batchJobExecution) {\n        batchJobMap.put(batchJobExecution.getBatchJobId(), batchJobExecution);\n    }\n\n    @Override\n    public BatchJobExecution getBatchJobExecutionByBatchJobId(String batchJobId) {\n        return batchJobMap.get(batchJobId);\n    }\n\n    @Override\n    public JobExecution saveJobExecution(JobExecution jobExecution) {\n        Assert.notNull(jobExecution, \"jobExecution cannot be null\");\n        Assert.notNull(jobExecution.getJobDef(), \"job cannot be null.\");\n        Assert.notNull(jobExecution.getJobId(), \"jobExecutionId cannot be null.\");\n        jobMap.put(jobExecution.getJobId(), jobExecution);\n        return jobExecution;\n    }\n\n    @Override\n    public void updateJobExecution(JobExecution jobExecution) {\n        Assert.notNull(jobExecution, \"jobExecution cannot be null\");\n        String executionId = jobExecution.getJobId();\n        Assert.notNull(executionId, \"jobExecutionId cannot be null.\");\n        JobExecution oldJobExecution = jobMap.get(executionId);\n        if (oldJobExecution == null) {\n            throw new JobException(\"No jobExecution with id:\" + executionId + \" found\");\n        }\n        jobMap.put(executionId, jobExecution);\n    }\n\n    @Override\n    public void saveStepExecution(StepExecution stepExecution) {\n        Assert.notNull(stepExecution.getJobExecution(), \"jobExecution cannot be null\");\n        String jobExecutionId = stepExecution.getJobExecution().getJobId();\n        String stepName = stepExecution.getStepName();\n        stepMap.put(jobExecutionId + stepName, stepExecution);\n        Set<StepExecution> stepSet = stepSetMap.get(jobExecutionId);\n        if (stepSet == null) {\n            stepSet = new HashSet<>();\n            stepSetMap.put(jobExecutionId, stepSet);\n        }\n        stepSet.add(stepExecution);\n    }\n\n    @Override\n    public void updateStepExecution(StepExecution stepExecution) {\n        Assert.notNull(stepExecution.getJobExecution(), \"jobExecution cannot be null\");\n        String jobExecutionId = stepExecution.getJobExecution().getJobId();\n        String stepName = stepExecution.getStepName();\n        stepMap.put(jobExecutionId + stepName, stepExecution);\n        Set<StepExecution> stepSet = stepSetMap.get(jobExecutionId);\n        for (StepExecution target : stepSet) {\n            if (target.equals(stepExecution)) {\n                BeanUtils.copyProperties(stepExecution, target);\n            }\n        }\n    }\n\n    @Override\n    public StepExecution getStepExecution(String jobExecutionId, String stepName) {\n        return stepMap.get(jobExecutionId + stepName);\n    }\n\n    @Override\n    public List<StepExecution> listStepExecutions(String jobExecutionId) {\n        return stepSetMap.get(jobExecutionId).stream().toList();\n    }\n\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/repository/redis/RedisJobRepository.java",
    "content": "package com.alibaba.cola.job.repository.redis;\n\n\nimport com.alibaba.cola.job.model.BatchJobExecution;\nimport com.alibaba.cola.job.model.JobExecution;\nimport com.alibaba.cola.job.model.StepExecution;\nimport com.alibaba.cola.job.repository.AbstractJobRepository;\nimport com.alibaba.cola.job.repository.JsonUtil;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport jakarta.annotation.PostConstruct;\nimport jakarta.annotation.Resource;\nimport org.springframework.data.redis.core.Cursor;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.core.ScanOptions;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class RedisJobRepository extends AbstractJobRepository {\n\n    @Resource\n    private RedisTemplate<String, JobExecution> jobRedisTemplate;\n\n    @Resource\n    private RedisTemplate<String, StepExecution> stepRedisTemplate;\n\n    @Resource\n    private RedisTemplate<String, BatchJobExecution> batchJobRedisTemplate;\n\n\n    private ObjectMapper objectMapper;\n\n    @PostConstruct\n    public void init() {\n        objectMapper = JsonUtil.ObjectMapperFactory.getObjectMapper();\n    }\n\n    public static final String JOB_PREFIX = \"job:\";\n\n    public static final String BATCH_JOB_PREFIX = \"batchJob:\";\n\n    @Override\n    public BatchJobExecution saveBatchJobExecution(BatchJobExecution batchJobExecution) {\n        String key = BATCH_JOB_PREFIX + batchJobExecution.getBatchJobId();\n        batchJobRedisTemplate.opsForValue().set(key, batchJobExecution);\n        return batchJobExecution;\n    }\n\n    @Override\n    public void updateBatchJobExecution(BatchJobExecution batchJobExecution) {\n        String key = BATCH_JOB_PREFIX + batchJobExecution.getBatchJobId();\n        batchJobRedisTemplate.opsForValue().set(key, batchJobExecution);\n    }\n\n    @Override\n    public BatchJobExecution getBatchJobExecutionByBatchJobId(String batchJobId) {\n        String key = BATCH_JOB_PREFIX + batchJobId;\n        return objectMapper.convertValue(batchJobRedisTemplate.opsForValue().get(key),\n                new TypeReference<BatchJobExecution>() { });\n    }\n\n    @Override\n    public JobExecution saveJobExecution(JobExecution jobExecution) {\n        String key = JOB_PREFIX + jobExecution.getJobId();\n        jobRedisTemplate.opsForValue().set(key, jobExecution);\n        return jobExecution;\n    }\n\n    @Override\n    public void updateJobExecution(JobExecution jobExecution) {\n        String key = JOB_PREFIX + jobExecution.getJobId();\n        jobRedisTemplate.opsForValue().set(key, jobExecution);\n    }\n\n    @Override\n    public JobExecution getJobExecutionByJobId(String jobId) {\n        String key = JOB_PREFIX + jobId;\n        return objectMapper.convertValue(jobRedisTemplate.opsForValue().get(key),\n                new TypeReference<JobExecution>() { });\n    }\n    @Override\n    public void saveStepExecution(StepExecution stepExecution) {\n        String key = JOB_PREFIX + stepExecution.getJobExecution().getJobId() + \":\" + stepExecution.getStepName();\n        stepRedisTemplate.opsForValue().set(key, stepExecution);\n    }\n\n    @Override\n    public void updateStepExecution(StepExecution stepExecution) {\n        String key = JOB_PREFIX + stepExecution.getJobExecution().getJobId() + \":\" + stepExecution.getStepName();\n        stepRedisTemplate.opsForValue().set(key, stepExecution);\n    }\n\n    @Override\n    public StepExecution getStepExecution(String jobId, String stepName) {\n        String key = JOB_PREFIX + jobId + \":\" + stepName;\n        return objectMapper.convertValue(stepRedisTemplate.opsForValue().get(key),\n                new TypeReference<StepExecution>() { });\n    }\n\n    @Override\n    public List<StepExecution> listStepExecutions(String jobExecutionId) {\n        List<StepExecution> stepExecutions = new ArrayList<>();\n        ScanOptions options = ScanOptions.scanOptions().match(JOB_PREFIX + jobExecutionId + \":*\").count(10) // 每次返回的数量\n                .build();\n\n        Cursor<String> cursor = jobRedisTemplate.scan(options);\n        while (cursor.hasNext()) {\n            StepExecution stepExecution = objectMapper.convertValue(stepRedisTemplate.opsForValue().get(cursor.next()),\n                    new TypeReference<StepExecution>() { });\n            stepExecutions.add(stepExecution);\n        }\n        return stepExecutions;\n    }\n\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/main/resources/schema-mysql.sql",
    "content": "CREATE TABLE IF NOT EXISTS BATCH_JOB_EXECUTION\n(\n  BATCH_JOB_ID          VARCHAR(128) NOT NULL PRIMARY KEY,\n  JOB_TYPE              VARCHAR(128),\n  JOB_EXECUTION_RESULTS TEXT,\n  EXECUTION_STATUS      VARCHAR(20),\n  START_TIME        DATETIME DEFAULT NULL,\n  END_TIME          DATETIME DEFAULT NULL\n);\n\nCREATE TABLE IF NOT EXISTS JOB_EXECUTION\n(\n  JOB_ID            VARCHAR(256) NOT NULL PRIMARY KEY,\n  BATCH_JOB_ID      VARCHAR(128),\n  START_TIME        DATETIME DEFAULT NULL,\n  UPDATE_TIME        DATETIME DEFAULT NULL,\n  END_TIME          DATETIME DEFAULT NULL,\n  EXECUTION_STATUS  VARCHAR(20),\n  EXECUTION_CONTEXT TEXT,\n  MESSAGE           TEXT\n);\n\nCREATE TABLE IF NOT EXISTS STEP_EXECUTION\n(\n  STEP_ID           VARCHAR(512) NOT NULL PRIMARY KEY,\n  STEP_NAME         VARCHAR(50) NOT NULL,\n  JOB_ID            VARCHAR(128) NOT NULL,\n  START_TIME        DATETIME DEFAULT NULL,\n  END_TIME          DATETIME DEFAULT NULL,\n  EXECUTION_STATUS  VARCHAR(20),\n  EXECUTION_CONTEXT TEXT,\n  MESSAGE           TEXT,\n  constraint JOB_EXEC_STEP_FK foreign key (JOB_ID)\n  references JOB_EXECUTION (JOB_ID)\n);\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/AbstractBaseJobTest.java",
    "content": "package com.alibaba.cola.job.test;\n\nimport com.alibaba.cola.job.BatchJobLauncher;\nimport com.alibaba.cola.job.ExecutionContext;\nimport com.alibaba.cola.job.JobBuilderFactory;\nimport com.alibaba.cola.job.JobLauncher;\nimport com.alibaba.cola.job.model.*;\nimport com.alibaba.cola.job.repository.JobRepository;\nimport com.alibaba.cola.job.test.steps.*;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Assertions;\n\nimport java.util.Map;\nimport java.util.concurrent.*;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport static org.awaitility.Awaitility.await;\n\n@Slf4j\npublic abstract class AbstractBaseJobTest {\n\n    public abstract JobRepository getJobRepository();\n\n    public abstract void testBatchJobCompleted();\n\n    public void batchJobCompleted() {\n        Job job = JobBuilderFactory.create()\n                .addStep(new MyStep1())\n                .addStep(new MyStep2())\n                .addStep(new LongTimeStep())\n                .build(\"batchJob\", getJobRepository());\n\n        ExecutionContext context1 = new ExecutionContext();\n        context1.putString(\"hostId\", \"111\");\n        JobInstance jobInstance1 = new JobInstance(job, context1);\n\n        ExecutionContext context2 = new ExecutionContext();\n        context2.putString(\"hostId\", \"222\");\n        JobInstance jobInstance2 = new JobInstance(job, context2);\n\n        ExecutionContext context3 = new ExecutionContext();\n        context3.putString(\"hostId\", \"333\");\n        JobInstance jobInstance3 = new JobInstance(job, context3);\n\n        BatchJob batchJob = new BatchJob();\n        batchJob.add(jobInstance1)\n                .add(jobInstance2)\n                .add(jobInstance3)\n                .jobRepository(getJobRepository())\n                .executorService(buildExecutorService());\n\n        BatchJobExecution batchJobExecution = BatchJobLauncher.execute(batchJob);\n\n        // 最长等待5秒，每1秒钟检查一下，看batchJob状态是不是COMPLETED。超过5秒，报错\n        await().atMost(7, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until(() -> {\n            boolean isCompleted = BatchJobLauncher.checkAndRefresh(batchJob, batchJobExecution.getBatchJobId());\n            return isCompleted;\n        });\n    }\n\n    public ExecutorService buildExecutorService() {\n        // 创建自定义的 ThreadFactory\n        ThreadFactory namedThreadFactory = new ThreadFactory() {\n            private final AtomicInteger count = new AtomicInteger(1);\n\n            @Override\n            public Thread newThread(Runnable r) {\n                Thread thread = new Thread(r);\n                thread.setName(\"CustomThread-\" + count.getAndIncrement());\n                return thread;\n            }\n        };\n\n        // 使用自定义的 ThreadFactory 创建 ExecutorService\n        ExecutorService executorService = new ThreadPoolExecutor(2, // corePoolSize\n                5, // maximumPoolSize\n                60L, // keepAliveTime\n                TimeUnit.SECONDS, // timeUnit\n                new LinkedBlockingQueue<>(), // workQueue\n                namedThreadFactory // 使用自定义的 ThreadFactory\n        );\n        return executorService;\n    }\n\n    public abstract void testBatchJobFailed();\n\n    public void batchJobFailed() {\n        Job job = JobBuilderFactory.create()\n                .addStep(new MyStep1())\n                .addStep(new MyStep2())\n                .addStep(new FailedStep())\n                .build(\"batchJob\", getJobRepository());\n\n        ExecutionContext context1 = new ExecutionContext();\n        context1.putString(\"hostId\", \"111\");\n        JobInstance jobInstance1 = new JobInstance(job, context1);\n\n        ExecutionContext context2 = new ExecutionContext();\n        context2.putString(\"hostId\", \"222\");\n        JobInstance jobInstance2 = new JobInstance(job, context2);\n\n        ExecutionContext context3 = new ExecutionContext();\n        context3.putString(\"hostId\", \"333\");\n        JobInstance jobInstance3 = new JobInstance(job, context3);\n\n        BatchJob batchJob = new BatchJob();\n        batchJob.add(jobInstance1).add(jobInstance2).add(jobInstance3).jobRepository(getJobRepository());\n\n        BatchJobExecution batchJobExecution = BatchJobLauncher.execute(batchJob);\n\n        // 最长等待5秒，每300ms钟检查一下，看batchJob状态是不是Failed\n        await().atMost(5, TimeUnit.SECONDS).pollInterval(300, TimeUnit.MILLISECONDS).until(() -> {\n            BatchJobLauncher.checkAndRefresh(batchJob, batchJobExecution.getBatchJobId());\n            BatchJobExecution result = getJobRepository().getBatchJobExecutionByBatchJobId(\n                    batchJobExecution.getBatchJobId());\n            return result.isFailed();\n        });\n    }\n\n    public abstract void testJobCompleted();\n\n    public void jobCompleted() {\n        JobBuilderFactory.JobBuilder jobBuilder = JobBuilderFactory.create();\n        jobBuilder.addStep(new MyStep1());\n        jobBuilder.addStep(new MyStep2());\n        Job job = jobBuilder.build(\"myJob\", getJobRepository());\n        JobExecution jobExecution = JobLauncher.executeSync(job);\n\n        jobExecution = getJobRepository().getJobExecutionByJobId(jobExecution.getJobId());\n        Assertions.assertEquals(ExecutionStatus.COMPLETED, jobExecution.getExecutionStatus());\n    }\n\n    public void jobRunning() {\n        JobBuilderFactory.JobBuilder jobBuilder = JobBuilderFactory.create();\n        jobBuilder.addStep(new MyStep1());\n        jobBuilder.addStep(new MyStep2());\n        jobBuilder.isAsync(true);\n        Job job = jobBuilder.build(\"myJob\", getJobRepository());\n        JobExecution jobExecution = JobLauncher.executeSync(job);\n\n        jobExecution = getJobRepository().getJobExecutionByJobId(jobExecution.getJobId());\n        Assertions.assertEquals(ExecutionStatus.RUNNING, jobExecution.getExecutionStatus());\n    }\n\n    public abstract void testJobWithCustomizedParam();\n\n    public void jobWithCustomizedParam() {\n        JobBuilderFactory.JobBuilder jobBuilder = JobBuilderFactory.create();\n        jobBuilder.addStep(new MyStep2());\n        Job job = jobBuilder.build(\"myJob\", getJobRepository());\n//        ExecutionContext<JobRequest> executionContext = new ExecutionContext<>();\n//        executionContext.setParam(JobRequest.mock());\n        ExecutionContext executionContext = new ExecutionContext();\n        JobExecution jobExecution = JobLauncher.executeSync(job, executionContext);\n\n        jobExecution = getJobRepository().getJobExecutionByJobId(jobExecution.getJobId());\n        Assertions.assertEquals(ExecutionStatus.COMPLETED, jobExecution.getExecutionStatus());\n    }\n\n    public abstract void testJobRerunSkipNext();\n\n    public void jobRerunSkipNext() {\n        JobBuilderFactory.JobBuilder jobBuilder = JobBuilderFactory.create();\n        jobBuilder.addStep(new MyStep1());\n        jobBuilder.addStep(new MyStep2());\n        Job job = jobBuilder.build(\"myJob\", getJobRepository());\n        JobExecution jobExecution = JobLauncher.executeSync(job);\n\n        ExecutionContext executionContext = new ExecutionContext(jobExecution.getJobId());\n        JobLauncher.executeSync(job, executionContext);\n\n        jobExecution = getJobRepository().getJobExecutionByJobId(jobExecution.getJobId());\n        Assertions.assertEquals(ExecutionStatus.COMPLETED, jobExecution.getExecutionStatus());\n    }\n\n    public abstract void testJobRerunFixPreviousFail();\n\n    public void jobRerunFixPreviousFail() {\n        JobBuilderFactory.JobBuilder jobBuilder = JobBuilderFactory.create();\n        jobBuilder.addStep(new MyStep1());\n        jobBuilder.addStep(new SwitchStep());\n        Job job = jobBuilder.build(\"myJob\", getJobRepository());\n\n        JobExecution jobExecution = JobLauncher.executeSync(job);\n        jobExecution = getJobRepository().getJobExecutionByJobId(jobExecution.getJobId());\n        Assertions.assertEquals(ExecutionStatus.FAILED, jobExecution.getExecutionStatus());\n\n        ExecutionContext executionContext = new ExecutionContext(jobExecution.getJobId());\n        JobLauncher.executeSync(job, executionContext);\n        jobExecution = getJobRepository().getJobExecutionByJobId(jobExecution.getJobId());\n        Assertions.assertEquals(ExecutionStatus.COMPLETED, jobExecution.getExecutionStatus());\n    }\n\n    public abstract void testStepRollBack();\n\n    public void stepRollBack() {\n        JobBuilderFactory.JobBuilder jobBuilder = JobBuilderFactory.create();\n        jobBuilder.addStep(new MyStep1());\n        jobBuilder.addStep(new MyStep2());\n        jobBuilder.addStep(new MyStep3());\n        Job job = jobBuilder.build(\"myJob\", getJobRepository());\n        JobExecution jobExecution = JobLauncher.executeSync(job);\n\n        StepExecution stepExecution = getJobRepository().getStepExecution(jobExecution.getJobId(),\n                MyStep3.class.getSimpleName());\n        Assertions.assertEquals(ExecutionStatus.ROLLBACK_COMPLETED, stepExecution.getExecutionStatus());\n\n        jobExecution = getJobRepository().getJobExecutionByJobId(jobExecution.getJobId());\n        Assertions.assertEquals(ExecutionStatus.FAILED, jobExecution.getExecutionStatus());\n    }\n\n    public abstract void testStepRollbackFailed();\n\n    public void stepRollbackFailed() {\n        JobBuilderFactory.JobBuilder jobBuilder = JobBuilderFactory.create();\n        jobBuilder.addStep(new MyStep1());\n        jobBuilder.addStep(new MyStep2());\n        jobBuilder.addStep(new MyStep4());\n        Job job = jobBuilder.build(\"myJob\", getJobRepository());\n        JobExecution jobExecution = JobLauncher.executeSync(job);\n\n        StepExecution stepExecution = getJobRepository().getStepExecution(jobExecution.getJobId(),\n                MyStep4.class.getSimpleName());\n        Assertions.assertEquals(ExecutionStatus.ROLLBACK_FAILED, stepExecution.getExecutionStatus());\n\n        jobExecution = getJobRepository().getJobExecutionByJobId(jobExecution.getJobId());\n        Assertions.assertEquals(ExecutionStatus.FAILED, jobExecution.getExecutionStatus());\n    }\n\n    public void jobRollBack() {\n        JobBuilderFactory.JobBuilder jobBuilder = JobBuilderFactory.create();\n        jobBuilder.addStep(new MyStep1());\n        jobBuilder.addStep(new MyStep2());\n        jobBuilder.addStep(new MyStep3());\n        Job job = jobBuilder.needRollback(true).build(\"myJob\", getJobRepository());\n        JobExecution jobExecution = JobLauncher.executeSync(job);\n\n        for (Step step : job.getSteps()) {\n            StepExecution stepExecution = getJobRepository().getStepExecution(jobExecution.getJobId(),\n                    step.getClass().getSimpleName());\n            Assertions.assertEquals(ExecutionStatus.ROLLBACK_COMPLETED, stepExecution.getExecutionStatus());\n        }\n        jobExecution = getJobRepository().getJobExecutionByJobId(jobExecution.getJobId());\n        Assertions.assertEquals(ExecutionStatus.ROLLBACK_COMPLETED, jobExecution.getExecutionStatus());\n    }\n\n    public void jobRollBackFailed() {\n        JobBuilderFactory.JobBuilder jobBuilder = JobBuilderFactory.create();\n\n        MyStep5 rollbackErrorStep = new MyStep5();\n        jobBuilder.addStep(new MyStep1());\n        jobBuilder.addStep(rollbackErrorStep);\n        jobBuilder.addStep(new MyStep3());\n        Job job = jobBuilder.needRollback(true).build(\"myJob\", getJobRepository());\n        JobExecution jobExecution = JobLauncher.executeSync(job);\n\n        expectedStatus(jobExecution.getJobId(),\n                Map.of(\"MyStep1\", ExecutionStatus.COMPLETED, \"MyStep5\", ExecutionStatus.ROLLBACK_FAILED, \"MyStep3\",\n                        ExecutionStatus.ROLLBACK_COMPLETED));\n\n        jobExecution = getJobRepository().getJobExecutionByJobId(jobExecution.getJobId());\n        Assertions.assertEquals(ExecutionStatus.ROLLBACK_FAILED, jobExecution.getExecutionStatus());\n\n        // now step status = COMPLETED, ROLLBACK_FAILED ,ROLLBACK_COMPLETED\n        // continue rollback will skip third, rollback second and first\n        log.info(\"start continue rollback\");\n        rollbackErrorStep.setRollbackThrow(false);\n\n        jobExecution.getExecutionContext().setJobId(jobExecution.getJobId());\n        jobExecution = JobLauncher.executeSync(job, jobExecution.getExecutionContext());\n\n        // 这里需要补充全部的状态检查\n        expectedStatus(jobExecution.getJobId(),\n                Map.of(\"MyStep1\", ExecutionStatus.ROLLBACK_COMPLETED, \"MyStep5\", ExecutionStatus.ROLLBACK_COMPLETED,\n                        \"MyStep3\", ExecutionStatus.ROLLBACK_COMPLETED));\n    }\n\n    public void expectedStatus(String executionId, Map<String, ExecutionStatus> ExecutionStatus) {\n        for (var entry : ExecutionStatus.entrySet()) {\n            StepExecution stepExecution = getJobRepository().getStepExecution(executionId, entry.getKey());\n            Assertions.assertEquals(entry.getValue(), stepExecution.getExecutionStatus());\n        }\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/MemoryDBJobTest.java",
    "content": "package com.alibaba.cola.job.test;\n\nimport com.alibaba.cola.job.config.EnableColaJob;\nimport com.alibaba.cola.job.repository.JobRepository;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.ActiveProfiles;\n\n@Slf4j\n@SpringBootTest(classes = TestApplication.class)\n@ActiveProfiles(\"h2-test\")\npublic class MemoryDBJobTest extends AbstractBaseJobTest {\n\n    @Resource\n    JobRepository jobRepository;\n\n    @Override\n    public JobRepository getJobRepository() {\n        return jobRepository;\n    }\n\n    @Test\n    public void testBatchJobCompleted() {\n        super.batchJobCompleted();\n    }\n\n    @Test\n    public void testBatchJobFailed() {\n        super.batchJobFailed();\n    }\n\n    @Test\n    public void testJobCompleted() {\n        super.jobCompleted();\n    }\n\n    @Test\n    public void testJobWithCustomizedParam() {\n        super.jobWithCustomizedParam();\n    }\n\n    @Test\n    public void testJobRerunSkipNext() {\n        super.jobRerunSkipNext();\n    }\n\n    @Test\n    public void testJobRerunFixPreviousFail() {\n        super.jobRerunFixPreviousFail();\n    }\n\n    @Test\n    public void testStepRollBack() {\n        super.stepRollBack();\n    }\n\n    @Test\n    public void testStepRollbackFailed() {\n        super.stepRollbackFailed();\n    }\n\n    @Test\n    public void testJobRollback() {\n        super.jobRollBack();\n    }\n\n    @Test\n    public void test() {\n        super.jobRollBackFailed();\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/MemoryJobTest.java",
    "content": "package com.alibaba.cola.job.test;\n\n\nimport com.alibaba.cola.job.repository.JobRepository;\nimport com.alibaba.cola.job.repository.memory.MemoryJobRepository;\nimport org.junit.jupiter.api.Test;\n\npublic class MemoryJobTest extends AbstractBaseJobTest {\n\n    private static JobRepository jobRepository = new MemoryJobRepository();\n\n    @Override\n    public JobRepository getJobRepository() {\n        return jobRepository;\n    }\n\n    @Test\n    public void testBatchJobCompleted() {\n        super.batchJobCompleted();\n    }\n\n    @Test\n    public void testBatchJobFailed() {\n        super.batchJobFailed();\n    }\n\n    @Test\n    public void testJobCompleted() {\n        super.jobCompleted();\n    }\n\n    @Test\n    public void testJobWithCustomizedParam() {\n        super.jobWithCustomizedParam();\n    }\n\n    @Test\n    public void testJobRerunSkipNext() {\n        super.jobRerunSkipNext();\n    }\n\n    @Test\n    public void testJobRerunFixPreviousFail() {\n        super.jobRerunFixPreviousFail();\n    }\n\n    @Test\n    public void testStepRollBack() {\n        super.stepRollBack();\n    }\n\n    @Test\n    public void testStepRollbackFailed() {\n        super.stepRollbackFailed();\n    }\n\n    @Test\n    public void testJobRollback() {\n        super.jobRollBack();\n    }\n\n    @Test\n    public void testJobRollbackFailed() {\n        super.jobRollBackFailed();\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/MySQLJobTest.java",
    "content": "package com.alibaba.cola.job.test;\n\nimport com.alibaba.cola.job.repository.JobRepository;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.ActiveProfiles;\n\n@Slf4j\n@SpringBootTest(classes = TestApplication.class)\n@ActiveProfiles(\"mysql-test\")\n@Disabled // MySQL tests are disabled by default, enable them manually if needed\npublic class MySQLJobTest extends AbstractBaseJobTest {\n\n    @Resource\n    JobRepository jobRepository;\n\n    @Override\n    public JobRepository getJobRepository() {\n        return jobRepository;\n    }\n\n    @Test\n    public void testBatchJobCompleted() {\n        super.batchJobCompleted();\n    }\n\n    @Test\n    public void testBatchJobFailed() {\n        super.batchJobFailed();\n    }\n\n    @Test\n    public void testJobCompleted() {\n        super.jobCompleted();\n    }\n\n    @Test\n    public void testJobWithCustomizedParam() {\n        super.jobWithCustomizedParam();\n    }\n\n    @Test\n    public void testJobRerunSkipNext() {\n        super.jobRerunSkipNext();\n    }\n\n    @Test\n    public void testJobRerunFixPreviousFail() {\n        super.jobRerunFixPreviousFail();\n    }\n\n    @Test\n    public void testStepRollBack() {\n        super.stepRollBack();\n    }\n\n    @Test\n    public void testStepRollbackFailed() {\n        super.stepRollbackFailed();\n    }\n\n    @Test\n    public void testJobRollback() {\n        super.jobRollBack();\n    }\n\n    @Test\n    public void test() {\n        super.jobRollBackFailed();\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/RedisJobTest.java",
    "content": "package com.alibaba.cola.job.test;\n\n\nimport com.alibaba.cola.job.JobBuilderFactory;\nimport com.alibaba.cola.job.JobLauncher;\nimport com.alibaba.cola.job.model.ExecutionStatus;\nimport com.alibaba.cola.job.model.Job;\nimport com.alibaba.cola.job.model.JobExecution;\nimport com.alibaba.cola.job.repository.JobRepository;\nimport com.alibaba.cola.job.test.steps.FailedStep;\nimport com.alibaba.cola.job.test.steps.MyStep1;\nimport com.alibaba.cola.unittest.redis.RedisExtension;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.ActiveProfiles;\n\n@Slf4j\n@SpringBootTest(classes = TestApplication.class)\n@ExtendWith(RedisExtension.class)\n@ActiveProfiles(\"redis-test\")\npublic class RedisJobTest extends AbstractBaseJobTest {\n\n    @Resource\n    JobRepository jobRepository;\n\n    @Override\n    public JobRepository getJobRepository() {\n        return jobRepository;\n    }\n\n    @Test\n    public void testBatchJobCompleted() {\n        super.batchJobCompleted();\n    }\n\n    @Test\n    public void testBatchJobFailed() {\n        super.batchJobFailed();\n    }\n\n    @Test\n    public void testJobCompleted() {\n        super.jobCompleted();\n    }\n\n    @Test\n    public void testJobRunning(){\n        super.jobRunning();\n    }\n\n    @Test\n    public void testJobFailed() {\n        JobBuilderFactory.JobBuilder jobBuilder = JobBuilderFactory.create();\n        jobBuilder.addStep(new MyStep1());\n        jobBuilder.addStep(new FailedStep());\n        Job job = jobBuilder.build(\"myJob\", getJobRepository());\n        JobExecution jobExecution = JobLauncher.executeSync(job);\n\n        jobExecution = getJobRepository().getJobExecutionByJobId(jobExecution.getJobId());\n        Assertions.assertEquals(ExecutionStatus.FAILED, jobExecution.getExecutionStatus());\n    }\n\n    @Test\n    public void testJobWithCustomizedParam() {\n        super.jobWithCustomizedParam();\n    }\n\n    @Test\n    public void testJobRerunSkipNext() {\n        super.jobRerunSkipNext();\n    }\n\n    @Test\n    public void testJobRerunFixPreviousFail() {\n        super.jobRerunFixPreviousFail();\n    }\n\n    @Test\n    public void testStepRollBack() {\n        super.stepRollBack();\n    }\n\n    @Test\n    public void testStepRollbackFailed() {\n        super.stepRollbackFailed();\n    }\n\n    @Test\n    public void testJobRollback() {\n        super.jobRollBack();\n    }\n\n    @Test\n    public void testJobRollBackFailed() {\n        super.jobRollBackFailed();\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/TestApplication.java",
    "content": "package com.alibaba.cola.job.test;\n\nimport com.alibaba.cola.job.config.EnableColaJob;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableColaJob\npublic class TestApplication {\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/TestsContainerBoot.java",
    "content": "package com.alibaba.cola.job.test;\n\nimport com.alibaba.cola.test.TestsContainer;\n\npublic class TestsContainerBoot {\n    public static void main(String[] args) {\n        TestsContainer.start();\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/steps/FailedStep.java",
    "content": "package com.alibaba.cola.job.test.steps;\n\nimport com.alibaba.cola.job.model.AbstractStep;\nimport com.alibaba.cola.job.model.StepExecution;\n\n// This class is used to simulate a step that fails during execution\npublic class FailedStep extends AbstractStep {\n    @Override\n    public void doExecute(StepExecution stepExecution) {\n        System.out.println(\"this is FailedStep\");\n        throw new RuntimeException(\"something wrong happened\");\n    }\n\n    @Override\n    public void doRollback(StepExecution stepExecution) {\n\n    }\n\n    @Override\n    public boolean needRollBack(StepExecution stepExecution) {\n        return false;\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/steps/LongTimeStep.java",
    "content": "package com.alibaba.cola.job.test.steps;\n\nimport com.alibaba.cola.job.model.AbstractStep;\nimport com.alibaba.cola.job.model.StepExecution;\n\n\n// This class represents a step that simulates a long execution time of 2 seconds.\npublic class LongTimeStep extends AbstractStep {\n    @Override\n    public void doExecute(StepExecution stepExecution) {\n        try {\n            System.out.println(\"This is long time executing step, will cost 2 seconds\");\n            Thread.sleep(2000);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public void doRollback(StepExecution stepExecution) {\n\n    }\n\n    @Override\n    public boolean needRollBack(StepExecution stepExecution) {\n        return false;\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/steps/MyStep1.java",
    "content": "package com.alibaba.cola.job.test.steps;\n\n\nimport com.alibaba.cola.job.model.AbstractStep;\nimport com.alibaba.cola.job.model.StepExecution;\n\npublic class MyStep1 extends AbstractStep {\n\n    @Override\n    public void doRollback(StepExecution stepExecution) {\n\n    }\n\n    @Override\n    public void doExecute(StepExecution stepExecution) {\n        System.out.println(\"this is step1\");\n        stepExecution.getExecutionContext().putString(\"step1\", \"something need pass to step2\");\n    }\n\n    @Override\n    public boolean needRollBack(StepExecution stepExecution) {\n        return true;\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/steps/MyStep2.java",
    "content": "package com.alibaba.cola.job.test.steps;\n\n\nimport com.alibaba.cola.job.model.AbstractStep;\nimport com.alibaba.cola.job.model.StepExecution;\n\npublic class MyStep2 extends AbstractStep {\n\n    @Override\n    public void doRollback(StepExecution stepExecution) {\n\n    }\n\n    @Override\n    public void doExecute(StepExecution stepExecution) {\n        System.out.println(\"this is step2, information from step1: \" + stepExecution.getExecutionContext().getString(\"step1\"));\n        stepExecution.getExecutionContext().putString(\"step2\", \"valuable information to next\");\n    }\n\n    @Override\n    public boolean needRollBack(StepExecution stepExecution) {\n        return true;\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/steps/MyStep3.java",
    "content": "package com.alibaba.cola.job.test.steps;\n\n\nimport com.alibaba.cola.job.model.AbstractStep;\nimport com.alibaba.cola.job.model.StepExecution;\n\npublic class MyStep3 extends AbstractStep {\n\n    @Override\n    public void doRollback(StepExecution stepExecution) {\n        System.out.println(\"this is step3 rollback\");\n    }\n\n    @Override\n    public void doExecute(StepExecution stepExecution) {\n        System.out.println(\"this is step3\");\n        throw new RuntimeException(\"something wrong happened\");\n    }\n\n    @Override\n    public boolean needRollBack(StepExecution stepExecution) {\n        return true;\n    }\n}\n\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/steps/MyStep4.java",
    "content": "package com.alibaba.cola.job.test.steps;\n\nimport com.alibaba.cola.job.model.AbstractStep;\nimport com.alibaba.cola.job.model.StepExecution;\n\npublic class MyStep4 extends AbstractStep {\n\n    @Override\n    public void doRollback(StepExecution stepExecution) {\n        System.out.println(\"this is step4 rollback\");\n        throw new RuntimeException(\"oops, rollback failed\");\n    }\n\n    @Override\n    public void doExecute(StepExecution stepExecution) {\n        System.out.println(\"this is step4\");\n        throw new RuntimeException(\"something wrong happened\");\n    }\n\n    @Override\n    public boolean needRollBack(StepExecution stepExecution) {\n        return true;\n    }\n}\n\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/steps/MyStep5.java",
    "content": "package com.alibaba.cola.job.test.steps;\n\n\nimport com.alibaba.cola.job.model.AbstractStep;\nimport com.alibaba.cola.job.model.StepExecution;\nimport lombok.Setter;\n\npublic class MyStep5 extends AbstractStep {\n\n    @Setter\n    private boolean isRollbackThrow = true;\n\n    @Override\n    public void doRollback(StepExecution stepExecution) {\n        System.out.println(\"this is step5 rollback\");\n        if(isRollbackThrow){\n            throw new RuntimeException(\"oops, rollback failed\");\n        }\n    }\n\n    @Override\n    public void doExecute(StepExecution stepExecution) {\n        System.out.println(\"this is step5\");\n    }\n\n    @Override\n    public boolean needRollBack(StepExecution stepExecution) {\n        return true;\n    }\n}\n\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/java/com/alibaba/cola/job/test/steps/SwitchStep.java",
    "content": "package com.alibaba.cola.job.test.steps;\n\nimport com.alibaba.cola.job.model.AbstractStep;\nimport com.alibaba.cola.job.model.StepExecution;\n\nimport lombok.extern.slf4j.Slf4j;\n\n@Slf4j\npublic class SwitchStep extends AbstractStep {\n    private static boolean switcher = true;\n\n    @Override\n    public void doExecute(StepExecution stepExecution) {\n        if(switcher){\n            switcher = false;\n            throw new RuntimeException(\"something wrong, rerun will fix it\");\n        }\n        log.info(\"flipped back to normal\");\n        switcher = true;\n    }\n\n    @Override\n    public void doRollback(StepExecution stepExecution) {\n\n    }\n\n    @Override\n    public boolean needRollBack(StepExecution stepExecution) {\n        return false;\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/resources/application-h2-test.yml",
    "content": "spring:\n  datasource:\n    url: jdbc:h2:mem:testdb\n    username: sa\n    password:\n    driver-class-name: org.h2.Driver\n  jpa:\n    database-platform: org.hibernate.dialect.H2Dialect\n    properties:\n      hibernate:\n        format_sql: true\n\ncola:\n  job:\n    repository-type: db\n    database:\n      auto-ddl: true\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/resources/application-mysql-test.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    # hard code for test purpose\n    url: jdbc:mysql://localhost:3306/colaJob?serverTimezone=UTC\n    username: root\n    password: root\n  jpa:\n    database-platform: org.hibernate.dialect.H2Dialect\n    properties:\n      hibernate:\n        format_sql: true\n\ncola:\n  job:\n    repository-type: db\n    database:\n      auto-ddl: true\n      ddl-location: schema-mysql.sql\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/resources/application-redis-test.yml",
    "content": "spring:\n  data:\n    redis:\n      host: localhost\n      port: 6379\n\ncola:\n  job:\n    repository-type: redis\n"
  },
  {
    "path": "cola-components/cola-component-job/src/test/resources/logback-test.xml",
    "content": "<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%date{HH:mm:ss.SSS} %highlight(%-5level) [%blue(%t)] %yellow(%C{45}): %msg%n%throwable</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <!--定义了两个appender，日志会通过往这两个appender里面写-->\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n\n    <!--应用日志-->\n    <logger name=\"com.alibaba.cola\" level=\"DEBUG\"/>\n\n    <!--\n        在application.yml中设置如下配置，可以用更加可读的方式输出SQL日志\n        spring.jpa.properties.hibernate.format_sql=true\n    -->\n    <!--JPA的SQL日志-->\n    <logger name=\"org.hibernate.SQL\" level=\"DEBUG\"/>\n    <!--JPA的SQL参数绑定日志-->\n    <logger name=\"org.hibernate.orm.jdbc.bind\" level=\"TRACE\"/>\n</configuration>\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/README.md",
    "content": "## 介绍\n这是COLA规则引擎\n\n## 使用\nhello world 案例：\n```java\n        RuleEngine ruleEngine = new DefaultRuleEngine();\n        Rule rule = new RuleBuilder()\n                .name(\"hello world rule\")\n                .description(\"always say hello world\")\n                .priority(1)\n                .when(facts -> true)\n                .then(facts -> System.out.println(\"hello world\"))\n                .build();\n        Rules rules = new Rules();\n        rules.register(rule);\n\n        ruleEngine.fire(rules, null);\n```\n\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/gitignore.txt",
    "content": "target/\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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    <parent>\n        <groupId>com.alibaba.cola</groupId>\n        <artifactId>cola-components-parent</artifactId>\n        <version>5.x-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>cola-component-ruleengine</artifactId>\n    <packaging>jar</packaging>\n    <name>${project.artifactId}:${project.version}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n        </dependency>\n\n        <!-- test dependencies -->\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-simple</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/api/Action.java",
    "content": "package com.alibaba.cola.ruleengine.api;\n\n@FunctionalInterface\npublic interface Action {\n    void execute(Facts facts);\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/api/Condition.java",
    "content": "package com.alibaba.cola.ruleengine.api;\n\nimport java.util.Objects;\n\n@FunctionalInterface\npublic interface Condition {\n\n    boolean evaluate(Facts facts);\n\n    //谓词and逻辑，参考Predicate\n    default Condition and(Condition other) {\n        Objects.requireNonNull(other);\n        return (facts) -> {\n            return this.evaluate(facts) && other.evaluate(facts);\n        };\n    }\n\n    //谓词or逻辑，参考Predicate\n    default Condition or(Condition other) {\n        Objects.requireNonNull(other);\n        return (facts) -> {\n            return this.evaluate(facts) || other.evaluate(facts);\n        };\n    }\n\n    /**\n     * A NoOp {@link Condition} that always returns false.\n     */\n    Condition FALSE = facts -> false;\n\n    /**\n     * A NoOp {@link Condition} that always returns true.\n     */\n    Condition TRUE = facts -> true;\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/api/Fact.java",
    "content": "package com.alibaba.cola.ruleengine.api;\n\nimport java.util.Objects;\n\npublic class Fact<T> {\n\n    private final String name;\n    private final T value;\n\n    /**\n     * Create a new fact.\n     * @param name of the fact\n     * @param value of the fact\n     */\n    public Fact(String name, T value) {\n        Objects.requireNonNull(name, \"name must not be null\");\n        Objects.requireNonNull(value, \"value must not be null\");\n        this.name = name;\n        this.value = value;\n    }\n\n    /**\n     * Get the fact name.\n     * @return fact name\n     */\n    public String getName() {\n        return name;\n    }\n\n    /**\n     * Get the fact value.\n     * @return fact value\n     */\n    public T getValue() {\n        return value;\n    }\n\n    @Override\n    public String toString() {\n        return \"Fact{\" +\n                \"name='\" + name + '\\'' +\n                \", value=\" + value +\n                '}';\n    }\n\n    /*\n     * The Facts API represents a namespace for facts where each fact has a unique name.\n     * Hence, equals/hashcode are deliberately calculated only on the fact name.\n     */\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        Fact<?> fact = (Fact<?>) o;\n        return name.equals(fact.name);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(name);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/api/Facts.java",
    "content": "package com.alibaba.cola.ruleengine.api;\n\nimport java.util.*;\n\npublic class Facts implements Iterable<Fact<?>> {\n\n    private final Set<Fact<?>> facts = new HashSet<>();\n\n    /**\n     * Add a fact, replacing any fact with the same name.\n     *\n     * @param name of the fact to add, must not be null\n     * @param value of the fact to add, must not be null\n     */\n    public <T> void put(String name, T value) {\n        Objects.requireNonNull(name, \"fact name must not be null\");\n        Objects.requireNonNull(value, \"fact value must not be null\");\n        Fact<?> retrievedFact = getFact(name);\n        if (retrievedFact != null) {\n            remove(retrievedFact);\n        }\n        add(new Fact<>(name, value));\n    }\n\n    /**\n     * Add a fact, replacing any fact with the same name.\n     *\n     * @param fact to add, must not be null\n     */\n    public <T> void add(Fact<T> fact) {\n        Objects.requireNonNull(fact, \"fact must not be null\");\n        Fact<?> retrievedFact = getFact(fact.getName());\n        if (retrievedFact != null) {\n            remove(retrievedFact);\n        }\n        facts.add(fact);\n    }\n\n    /**\n     * Remove a fact by name.\n     *\n     * @param factName name of the fact to remove, must not be null\n     */\n    public void remove(String factName) {\n        Objects.requireNonNull(factName, \"fact name must not be null\");\n        Fact<?> fact = getFact(factName);\n        if (fact != null) {\n            remove(fact);\n        }\n    }\n\n    /**\n     * Remove a fact.\n     *\n     * @param fact to remove, must not be null\n     */\n    public <T> void remove(Fact<T> fact) {\n        Objects.requireNonNull(fact, \"fact must not be null\");\n        facts.remove(fact);\n    }\n\n    /**\n     * Get the value of a fact by its name. This is a convenience method provided\n     * as a short version of {@code getFact(factName).getValue()}.\n     *\n     * @param factName name of the fact, must not be null\n     * @param <T> type of the fact's value\n     * @return the value of the fact having the given name, or null if there is\n     * no fact with the given name\n     */\n    @SuppressWarnings(\"unchecked\")\n    public <T> T get(String factName) {\n        Objects.requireNonNull(factName, \"fact name must not be null\");\n        Fact<?> fact = getFact(factName);\n        if (fact != null) {\n            return (T) fact.getValue();\n        }\n        return null;\n    }\n\n    /**\n     * Get a fact by name.\n     *\n     * @param factName name of the fact, must not be null\n     * @return the fact having the given name, or null if there is no fact with the given name\n     */\n    public Fact<?> getFact(String factName) {\n        Objects.requireNonNull(factName, \"fact name must not be null\");\n        return facts.stream()\n                .filter(fact -> fact.getName().equals(factName))\n                .findFirst()\n                .orElse(null);\n    }\n\n    public boolean contains(String factName){\n        return getFact(factName) != null;\n    }\n\n    public boolean contains(Fact fact){\n        if(fact == null){\n            return false;\n        }\n        return getFact(fact.getName()) != null;\n    }\n\n    public int size(){\n        return facts.size();\n    }\n\n    /**\n     * Return a copy of the facts as a map. It is not intended to manipulate\n     * facts outside of the rules engine (aka other than manipulating them through rules).\n     *\n     * @return a copy of the current facts as a {@link HashMap}\n     */\n    public Map<String, Object> asMap() {\n        Map<String, Object> map = new HashMap<>();\n        for (Fact<?> fact : facts) {\n            map.put(fact.getName(), fact.getValue());\n        }\n        return map;\n    }\n\n    /**\n     * Return an iterator on the set of facts. It is not intended to remove\n     * facts using this iterator outside of the rules engine (aka other than doing it through rules)\n     *\n     * @return an iterator on the set of facts\n     */\n    @Override\n    public Iterator<Fact<?>> iterator() {\n        return facts.iterator();\n    }\n\n    /**\n     * Clear facts.\n     */\n    public void clear() {\n        facts.clear();\n    }\n\n    @Override\n    public String toString() {\n        Iterator<Fact<?>> iterator = facts.iterator();\n        StringBuilder stringBuilder = new StringBuilder(\"[\");\n        while (iterator.hasNext()) {\n            stringBuilder.append(iterator.next().toString());\n            if (iterator.hasNext()) {\n                stringBuilder.append(\",\");\n            }\n        }\n        stringBuilder.append(\"]\");\n        return stringBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/api/Rule.java",
    "content": "package com.alibaba.cola.ruleengine.api;\n\npublic interface Rule extends Comparable<Rule> {\n\n    /**\n     * Default rule name.\n     */\n    String DEFAULT_NAME = \"rule\";\n\n    /**\n     * Default rule description.\n     */\n    String DEFAULT_DESCRIPTION = \"description\";\n\n    /**\n     * Default rule priority.\n     */\n    int DEFAULT_PRIORITY = Integer.MAX_VALUE - 1;\n\n    /**\n     * Getter for rule name.\n     * @return the rule name\n     */\n    default String getName() {\n        return DEFAULT_NAME;\n    }\n\n    /**\n     * Getter for rule description.\n     * @return rule description\n     */\n    default String getDescription() {\n        return DEFAULT_DESCRIPTION;\n    }\n\n    /**\n     * Getter for rule priority.\n     * @return rule priority\n     */\n    default int getPriority() {\n        return DEFAULT_PRIORITY;\n    }\n\n    /**\n     * This method implements the rule's condition(s).\n     * <strong>Implementations should handle any runtime exception and return true/false accordingly</strong>\n     *\n     * @return true if the rule should be applied given the provided facts, false otherwise\n     */\n    boolean evaluate(Facts facts);\n\n    /**\n     * This method implements the rule's action(s).\n     * @throws Exception thrown if an exception occurs when performing action(s)\n     */\n    void execute(Facts facts);\n\n    /**\n     * This method apply facts to the rule, which is the combination of evaluation and execution\n     * @param facts\n     * @return true if this rule is applied successfully, false otherwise\n     */\n    boolean apply(Facts facts);\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/api/RuleEngine.java",
    "content": "package com.alibaba.cola.ruleengine.api;\n\npublic interface RuleEngine {\n    /**\n     * Fire rule on given facts.\n     */\n    void fire(Rule rule, Facts facts);\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/core/AbstractRule.java",
    "content": "/*\n * The MIT License\n *\n *  Copyright (c) 2021, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)\n *\n *  Permission is hereby granted, free of charge, to any person obtaining a copy\n *  of this software and associated documentation files (the \"Software\"), to deal\n *  in the Software without restriction, including without limitation the rights\n *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n *  copies of the Software, and to permit persons to whom the Software is\n *  furnished to do so, subject to the following conditions:\n *\n *  The above copyright notice and this permission notice shall be included in\n *  all copies or substantial portions of the Software.\n *\n *  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n *  THE SOFTWARE.\n */\npackage com.alibaba.cola.ruleengine.core;\n\nimport com.alibaba.cola.ruleengine.api.Facts;\nimport com.alibaba.cola.ruleengine.api.Rule;\n\nimport java.util.Objects;\n\n/**\n * Basic rule implementation class that provides common methods.\n * <p>\n * You can extend this class and override {@link AbstractRule#evaluate(Facts)} and {@link AbstractRule#execute(Facts)} to provide rule\n * conditions and actions logic.\n *\n * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)\n */\npublic abstract class AbstractRule implements Rule {\n\n    /**\n     * Rule name.\n     */\n    protected String name;\n\n    /**\n     * Rule description.\n     */\n    protected String description;\n\n    /**\n     * Rule priority.\n     */\n    protected int priority;\n\n    /**\n     * Create a new {@link AbstractRule}.\n     */\n    public AbstractRule() {\n        this(Rule.DEFAULT_NAME, Rule.DEFAULT_DESCRIPTION, Rule.DEFAULT_PRIORITY);\n    }\n\n    /**\n     * Create a new {@link AbstractRule}.\n     *\n     * @param name rule name\n     */\n    public AbstractRule(final String name) {\n        this(name, Rule.DEFAULT_DESCRIPTION, Rule.DEFAULT_PRIORITY);\n    }\n\n    public AbstractRule(final int priority) {\n        this(Rule.DEFAULT_NAME, Rule.DEFAULT_DESCRIPTION, priority);\n    }\n\n    /**\n     * Create a new {@link AbstractRule}.\n     *\n     * @param name        rule name\n     * @param description rule description\n     */\n    public AbstractRule(final String name, final String description) {\n        this(name, description, Rule.DEFAULT_PRIORITY);\n    }\n\n    /**\n     * Create a new {@link AbstractRule}.\n     *\n     * @param name        rule name\n     * @param description rule description\n     * @param priority    rule priority\n     */\n    public AbstractRule(final String name, final String description, final int priority) {\n        this.name = name;\n        this.description = description;\n        this.priority = priority;\n    }\n\n    public abstract boolean evaluate(Facts facts);\n\n    public abstract void execute(Facts facts);\n\n    public abstract boolean apply(Facts facts);\n\n    public String getName() {\n        return name;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    public int getPriority() {\n        return priority;\n    }\n\n    public void setPriority(final int priority) {\n        this.priority = priority;\n    }\n\n    /*\n     * Rules are unique according to their names within a rules engine registry.\n     */\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o)\n            return true;\n        if (o == null || getClass() != o.getClass())\n            return false;\n\n        AbstractRule basicRule = (AbstractRule) o;\n\n        if (priority != basicRule.priority)\n            return false;\n        if (!name.equals(basicRule.name))\n            return false;\n        return Objects.equals(description, basicRule.description);\n\n    }\n\n    @Override\n    public int hashCode() {\n        int result = name.hashCode();\n        result = 31 * result + (description != null ? description.hashCode() : 0);\n        result = 31 * result + priority;\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return name + \":\" + priority;\n    }\n\n    @Override\n    public int compareTo(final Rule rule) {\n        if (getPriority() < rule.getPriority()) {\n            return -1;\n        } else if (getPriority() > rule.getPriority()) {\n            return 1;\n        }\n        return 0;\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/core/AllRules.java",
    "content": "package com.alibaba.cola.ruleengine.core;\n\nimport com.alibaba.cola.ruleengine.api.Facts;\nimport com.alibaba.cola.ruleengine.api.Rule;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Collections;\n\npublic class AllRules  extends CompositeRule{\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(AllRules.class);\n\n\n    public static CompositeRule allOf(Rule... rules) {\n        CompositeRule instance = new AllRules();\n        Collections.addAll(instance.rules, rules);\n        return instance;\n    }\n\n    @Override\n    public boolean evaluate(Facts facts) {\n        if (rules.stream().allMatch(rule -> rule.evaluate(facts)))\n            return true;\n        return false;\n    }\n\n    @Override\n    public void execute(Facts facts) {\n        for (Rule rule : rules) {\n            rule.execute(facts);\n        }\n    }\n\n    @Override\n    protected boolean doApply(Facts facts) {\n        LOGGER.debug(\"start AND composite rule apply\");\n        if (evaluate(facts)) {\n            for (Rule rule : rules) {\n                //所有的rules都执行\n                rule.execute(facts);\n            }\n            return true;\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/core/AnyRules.java",
    "content": "package com.alibaba.cola.ruleengine.core;\n\nimport com.alibaba.cola.ruleengine.api.Facts;\nimport com.alibaba.cola.ruleengine.api.Rule;\n\nimport java.util.Collections;\n\npublic class AnyRules extends CompositeRule{\n\n    public static CompositeRule anyOf(Rule... rules) {\n        CompositeRule instance = new AnyRules();\n        Collections.addAll(instance.rules, rules);\n        return instance;\n    }\n\n    @Override\n    public boolean evaluate(Facts facts) {\n        if (rules.stream().anyMatch(rule -> rule.evaluate(facts)))\n            return true;\n        return false;\n    }\n\n    @Override\n    public void execute(Facts facts) {\n        //不支持OR relation\n        throw new RuntimeException(\"execute not supported for OR relation composite\");\n    }\n\n    @Override\n    protected boolean doApply(Facts facts) {\n        for (Rule rule : rules) {\n            //短路操作，只要第一个rule成功执行，其它就不执行了\n            if (rule.apply(facts)) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/core/CompositeRule.java",
    "content": "package com.alibaba.cola.ruleengine.core;\n\nimport com.alibaba.cola.ruleengine.api.Facts;\nimport com.alibaba.cola.ruleengine.api.Rule;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic abstract class CompositeRule extends AbstractRule {\n    private static final Logger LOGGER = LoggerFactory.getLogger(CompositeRule.class);\n\n    protected List<Rule> rules = new ArrayList<>();\n\n    private boolean isSorted=false;\n\n    public CompositeRule priority(int priority) {\n        this.priority = priority;\n        return this;\n    }\n\n    public CompositeRule name(String name){\n        this.name = name;\n        return this;\n    }\n\n    public CompositeRule() {\n\n    }\n\n    @Override\n    public boolean apply(Facts facts) {\n        sort();\n        return doApply(facts);\n    }\n\n    protected abstract boolean doApply(Facts facts);\n\n    protected void sort(){\n        if(!isSorted){\n            LOGGER.debug(this.name+\" before sort:\" + rules);\n            Collections.sort(rules);\n            LOGGER.debug(this.name+\" after sort:\" + rules);\n            isSorted = true;\n        }\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/core/DefaultRule.java",
    "content": "/*\n * The MIT License\n *\n *  Copyright (c) 2021, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)\n *\n *  Permission is hereby granted, free of charge, to any person obtaining a copy\n *  of this software and associated documentation files (the \"Software\"), to deal\n *  in the Software without restriction, including without limitation the rights\n *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n *  copies of the Software, and to permit persons to whom the Software is\n *  furnished to do so, subject to the following conditions:\n *\n *  The above copyright notice and this permission notice shall be included in\n *  all copies or substantial portions of the Software.\n *\n *  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n *  THE SOFTWARE.\n */\npackage com.alibaba.cola.ruleengine.core;\n\nimport com.alibaba.cola.ruleengine.api.Action;\nimport com.alibaba.cola.ruleengine.api.Condition;\nimport com.alibaba.cola.ruleengine.api.Facts;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class DefaultRule extends AbstractRule {\n\n    private final Condition condition;\n    private final List<Action> actions;\n\n   public DefaultRule(Condition condition, Action action){\n        this.condition = condition;\n        this.actions = new ArrayList<>();\n        this.actions.add(action);\n    }\n\n    public DefaultRule(Condition condition, List<Action> actions){\n        this.condition = condition;\n        this.actions = actions;\n    }\n\n    public DefaultRule(String name, String description, int priority, Condition condition, List<Action> actions) {\n        super(name, description, priority);\n        this.condition = condition;\n        this.actions = actions;\n    }\n\n    @Override\n    public boolean evaluate(Facts facts) {\n        return condition.evaluate(facts);\n    }\n\n    @Override\n    public void execute(Facts facts) {\n        for (Action action : actions) {\n            action.execute(facts);\n        }\n    }\n\n    @Override\n    public boolean apply(Facts facts){\n       if (condition.evaluate(facts)){\n           for (Action action : actions) {\n               action.execute(facts);\n           }\n           return true;\n       }\n       return false;\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/core/DefaultRuleEngine.java",
    "content": "package com.alibaba.cola.ruleengine.core;\n\nimport com.alibaba.cola.ruleengine.api.Facts;\nimport com.alibaba.cola.ruleengine.api.Rule;\nimport com.alibaba.cola.ruleengine.api.RuleEngine;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class DefaultRuleEngine implements RuleEngine {\n    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRuleEngine.class);\n\n    @Override\n    public void fire(Rule rule, Facts facts) {\n        if (rule == null) {\n            LOGGER.error(\"Rules is null! Nothing to apply\");\n            return;\n        }\n        rule.apply(facts);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/core/NaturalRules.java",
    "content": "package com.alibaba.cola.ruleengine.core;\n\nimport com.alibaba.cola.ruleengine.api.Facts;\nimport com.alibaba.cola.ruleengine.api.Rule;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Collections;\n\n/**\n * This is Natural Rules\n */\npublic class NaturalRules extends CompositeRule{\n    private static final Logger LOGGER = LoggerFactory.getLogger(NaturalRules.class);\n\n    public static CompositeRule of(Rule... rules) {\n        CompositeRule instance = new NaturalRules();\n        Collections.addAll(instance.rules, rules);\n        return instance;\n    }\n\n    @Override\n    public boolean evaluate(Facts facts) {\n        //不支持, which means Natural Rules can not be the children of other rules\n        throw new RuntimeException(\"evaluate not supported for natural composite\");\n    }\n\n    @Override\n    public void execute(Facts facts) {\n        //不支持, which means Natural Rules can not be the children of other rules\n        throw new RuntimeException(\"execute not supported for natural composite\");\n    }\n\n    @Override\n    protected boolean doApply(Facts facts) {\n        LOGGER.debug(\"start Natural composite rule apply\");\n        for (Rule rule : rules) {\n            rule.apply(facts);\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/main/java/com/alibaba/cola/ruleengine/core/RuleBuilder.java",
    "content": "package com.alibaba.cola.ruleengine.core;\n\nimport com.alibaba.cola.ruleengine.api.Action;\nimport com.alibaba.cola.ruleengine.api.Condition;\nimport com.alibaba.cola.ruleengine.api.Rule;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class RuleBuilder {\n\n    private String name = Rule.DEFAULT_NAME;\n    private String description = Rule.DEFAULT_DESCRIPTION;\n    private int priority = Rule.DEFAULT_PRIORITY;\n\n    private Condition condition = Condition.FALSE;\n    private final List<Action> actions = new ArrayList<>();\n\n    /**\n     * Set rule name.\n     *\n     * @param name of the rule\n     * @return the builder instance\n     */\n    public RuleBuilder name(String name) {\n        this.name = name;\n        return this;\n    }\n\n    /**\n     * Set rule description.\n     *\n     * @param description of the rule\n     * @return the builder instance\n     */\n    public RuleBuilder description(String description) {\n        this.description = description;\n        return this;\n    }\n\n    /**\n     * Set rule priority.\n     *\n     * @param priority of the rule\n     * @return the builder instance\n     */\n    public RuleBuilder priority(int priority) {\n        this.priority = priority;\n        return this;\n    }\n\n    /**\n     * Set rule condition.\n     *\n     * @param condition of the rule\n     * @return the builder instance\n     */\n    public RuleBuilder when(Condition condition) {\n        this.condition = condition;\n        return this;\n    }\n\n    /**\n     * Add an action to the rule.\n     *\n     * @param action to add\n     * @return the builder instance\n     */\n    public RuleBuilder then(Action action) {\n        this.actions.add(action);\n        return this;\n    }\n\n    /**\n     * Create a new {@link Rule}.\n     *\n     * @return a new rule instance\n     */\n    public Rule build() {\n        return new DefaultRule(name, description, priority, condition, actions);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/CompositeRuleTest.java",
    "content": "package com.alibaba.cola.ruleengine;\n\nimport com.alibaba.cola.ruleengine.api.Facts;\nimport com.alibaba.cola.ruleengine.api.Rule;\nimport com.alibaba.cola.ruleengine.api.RuleEngine;\nimport com.alibaba.cola.ruleengine.core.*;\nimport static org.junit.jupiter.api.Assertions.*;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\npublic class CompositeRuleTest {\n\n    RuleEngine fizzBuzzEngine;\n\n    @BeforeEach\n    public void setUp(){\n        fizzBuzzEngine = new DefaultRuleEngine();\n    }\n\n\n\n    @Test\n    public void test_fizz_first(){\n        Facts facts = new Facts();\n        facts.put(\"number\", 15);\n\n        Rule rule = assembleRules(1,2,3);\n\n        fizzBuzzEngine.fire(rule, facts);\n        assertEquals(facts.getFact(\"fizz\").getValue(), \"Fizz\");\n    }\n\n    @Test\n    public void test_buzz_first(){\n        Facts facts = new Facts();\n        facts.put(\"number\", 15);\n\n        Rule rule = assembleRules(2,1,3);\n\n        fizzBuzzEngine.fire(rule, facts);\n        assertEquals(facts.getFact(\"buzz\").getValue(), \"Buzz\");\n    }\n\n    @Test\n    public void test_fizzBuzz_first(){\n        Facts facts = new Facts();\n        facts.put(\"number\", 15);\n\n        Rule rule = assembleRules(2,3,1);\n\n        fizzBuzzEngine.fire(rule, facts);\n        assertEquals(facts.getFact(\"fizz\").getValue(), \"Fizz\");\n        assertEquals(facts.getFact(\"buzz\").getValue(), \"Buzz\");\n    }\n\n\n    private Rule assembleRules(int fizzPriority, int BuzzPriority, int FizzBuzzPriority){\n        // create rules\n        Rule fizzRule = new RuleBuilder()\n                .name(\"fizzRule\")\n                .description(\"fizz rule when input times 3, output is Fizz\")\n                .priority(fizzPriority)\n                .when(facts -> (int) facts.get(\"number\") % 3 == 0)\n                .then(facts -> facts.put(\"fizz\",\"Fizz\"))\n                .build();\n\n        Rule buzzRule = new RuleBuilder()\n                .name(\"buzzRule\")\n                .description(\"buzz rule when input times 5, output is buzz\")\n                .priority(BuzzPriority)\n                .when(facts -> (int) facts.get(\"number\") % 5 == 0)\n                .then(facts -> facts.put(\"buzz\",\"Buzz\"))\n                .build();\n\n\n        Rule fizzBuzzRule = AllRules.allOf(fizzRule, buzzRule)\n                .name(\"fizzBuzzRule\")\n                .priority(FizzBuzzPriority);\n\n        Rule defaultRule = new RuleBuilder()\n                .name(\"defaultRule\")\n                .description(\"default rule, output number\")\n                .priority(40)\n                .when(facts -> true)\n                .then(facts -> System.out.print((int) facts.get(\"number\")))\n                .build();\n\n        Rule rule = AnyRules.anyOf(fizzBuzzRule, fizzRule, buzzRule, defaultRule)\n                .name(\"anyRule\");\n\n        return rule;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/FactsTest.java",
    "content": "package com.alibaba.cola.ruleengine;\n\nimport com.alibaba.cola.ruleengine.api.Fact;\nimport com.alibaba.cola.ruleengine.api.Facts;\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nimport java.util.Map;\n\npublic class FactsTest {\n\n    private final Facts facts = new Facts();\n\n    @Test\n    public void factsMustHaveUniqueName() {\n        facts.add(new Fact<>(\"foo\", 1));\n        facts.add(new Fact<>(\"foo\", 2));\n\n\n        Fact<?> fact = facts.getFact(\"foo\");\n        assertEquals(fact.getValue(),2);\n    }\n\n    @Test\n    public void testAdd() {\n        Fact<Integer> fact1 = new Fact<>(\"foo\", 1);\n        Fact<Integer> fact2 = new Fact<>(\"bar\", 2);\n        facts.add(fact1);\n        facts.add(fact2);\n\n        assertTrue(facts.contains(fact1));\n        assertTrue(facts.contains(fact2));\n    }\n\n    @Test\n    public void testPut() {\n        facts.put(\"foo\", 1);\n        facts.put(\"bar\", 2);\n\n        assertTrue(facts.contains(new Fact<>(\"foo\", 1)));\n        assertTrue(facts.contains(new Fact<>(\"bar\", 2)));\n    }\n\n    @Test\n    public void testRemove() {\n        Fact<Integer> foo = new Fact<>(\"foo\", 1);\n        facts.add(foo);\n        facts.remove(foo);\n\n        assertTrue(facts.size() == 0);\n    }\n\n    @Test\n    public void testRemoveByName() {\n        Fact<Integer> foo = new Fact<>(\"foo\", 1);\n        facts.add(foo);\n        facts.remove(\"foo\");\n\n        assertTrue(facts.size() == 0);\n    }\n\n    @Test\n    public void testGet() {\n        Fact<Integer> fact = new Fact<>(\"foo\", 1);\n        facts.add(fact);\n        Integer value = facts.get(\"foo\");\n        assertEquals(value, 1);\n    }\n\n    @Test\n    public void testGetFact() {\n        Fact<Integer> fact = new Fact<>(\"foo\", 1);\n        facts.add(fact);\n        Fact<?> retrievedFact = facts.getFact(\"foo\");\n        assertEquals(retrievedFact, fact);\n    }\n\n    @Test\n    public void testAsMap() {\n        Fact<Integer> fact1 = new Fact<>(\"foo\", 1);\n        Fact<Integer> fact2 = new Fact<>(\"bar\", 2);\n        facts.add(fact1);\n        facts.add(fact2);\n        Map<String, Object> map = facts.asMap();\n        assertTrue(map.containsKey(\"foo\"));\n        assertTrue(map.containsKey(\"bar\"));\n    }\n\n    @Test\n    public void testClear() {\n        Facts facts = new Facts();\n        facts.add(new Fact<>(\"foo\", 1));\n        facts.clear();\n\n        assertTrue(facts.size() == 0);\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/FizzBuzz.java",
    "content": "package com.alibaba.cola.ruleengine;\n\nimport com.alibaba.cola.ruleengine.api.Facts;\nimport com.alibaba.cola.ruleengine.api.Rule;\nimport com.alibaba.cola.ruleengine.api.RuleEngine;\nimport com.alibaba.cola.ruleengine.core.AllRules;\nimport com.alibaba.cola.ruleengine.core.AnyRules;\nimport com.alibaba.cola.ruleengine.core.DefaultRuleEngine;\nimport com.alibaba.cola.ruleengine.core.RuleBuilder;\n\npublic class FizzBuzz {\n    public static void main(String[] args) {\n        Rule fizzBuzzRule = assembleFizzBuzzRule();\n        RuleEngine ruleEngine = new DefaultRuleEngine();\n        for (int i = 0; i <= 15; i++) {\n            Facts facts = new Facts();\n            facts.put(\"number\", i);\n            ruleEngine.fire(fizzBuzzRule, facts);\n            System.out.println();\n        }\n    }\n\n    public static Rule assembleFizzBuzzRule() {\n        Rule fizzRule = new RuleBuilder()\n                .name(\"fizzRule\")\n                .description(\"fizz rule when input times 3, output is Fizz\")\n                .priority(10)\n                .when(facts -> (int) facts.get(\"number\") % 3 == 0)\n                .then(facts -> System.out.print(\"Fizz\"))\n                .build();\n\n        Rule buzzRule = new RuleBuilder()\n                .name(\"buzzRule\")\n                .description(\"buzz rule when input times 5, output is buzz\")\n                .priority(20)\n                .when(facts -> (int) facts.get(\"number\") % 5 == 0)\n                .then(facts -> System.out.print(\"Buzz\"))\n                .build();\n\n        Rule fizzBuzzRule = AllRules.allOf(fizzRule, buzzRule)\n                .name(\"fizzBuzzRule\")\n                .priority(1);\n\n        Rule defaultRule = new RuleBuilder()\n                .name(\"defaultRule\")\n                .description(\"default rule, output number\")\n                .priority(40)\n                .when(facts -> true)\n                .then(facts -> System.out.print((int) facts.get(\"number\")))\n                .build();\n\n        Rule rule = AnyRules.anyOf(fizzBuzzRule, fizzRule, buzzRule, defaultRule)\n                .name(\"anyRule\");\n\n        return rule;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/HelloWorld.java",
    "content": "package com.alibaba.cola.ruleengine;\n\nimport com.alibaba.cola.ruleengine.api.Rule;\nimport com.alibaba.cola.ruleengine.api.RuleEngine;\nimport com.alibaba.cola.ruleengine.core.DefaultRuleEngine;\nimport com.alibaba.cola.ruleengine.core.RuleBuilder;\n\npublic class HelloWorld {\n    public static void main(String[] args) {\n        RuleEngine ruleEngine = new DefaultRuleEngine();\n        Rule rule = new RuleBuilder()\n                .name(\"hello world rule\")\n                .description(\"always say hello world\")\n                .priority(1)\n                .when(facts -> true)\n                .then(facts -> System.out.println(\"hello world\"))\n                .build();\n\n        ruleEngine.fire(rule, null);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/PriorityTest.java",
    "content": "package com.alibaba.cola.ruleengine;\n\nimport com.alibaba.cola.ruleengine.api.Facts;\nimport com.alibaba.cola.ruleengine.api.Rule;\nimport com.alibaba.cola.ruleengine.api.RuleEngine;\nimport com.alibaba.cola.ruleengine.core.DefaultRule;\nimport com.alibaba.cola.ruleengine.core.DefaultRuleEngine;\nimport com.alibaba.cola.ruleengine.core.NaturalRules;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\n\npublic class PriorityTest {\n\n    RuleEngine ruleEngine;\n\n    @BeforeEach\n    public void setUp() {\n        ruleEngine = new DefaultRuleEngine();\n    }\n\n\n    @Test\n    public void testNoPriority() {\n        DummyRule r1 = new DummyRule(1);\n        DummyRule r2 = new DummyRule(2);\n        DummyRule r3 = new DummyRule(3);\n\n//        assertThat(rules).startsWith(r1).endsWith(r3);\n    }\n\n    @Test\n    public void testPriority() {\n        DummyRule r1 = new DummyRule(10);\n        DummyRule r2 = new DummyRule(3);\n        DummyRule r3 = new DummyRule(1);\n//        assertThat(rules).startsWith(r3).endsWith(r1);\n    }\n\n    @Test\n    public void test_natural_rule() {\n        DummyRule r1 = new DummyRule(10);\n        DummyRule r2 = new DummyRule(3);\n        DummyRule r3 = new DummyRule(1);\n        Facts facts = new Facts();\n        facts.put(\"number\", 15);\n\n        Rule naturalRules = NaturalRules.of(r1, r2, r3);\n        ruleEngine.fire(naturalRules, facts);\n    }\n\n\n    static class DummyRule extends DefaultRule {\n\n\n        public DummyRule(int priority) {\n            super(\"rule\" + priority, null, priority, facts -> true, new ArrayList<>());\n        }\n\n        @Override\n        public boolean evaluate(Facts facts) {\n            return true;\n        }\n\n        @Override\n        public void execute(Facts facts) {\n            System.out.println(facts.getFact(\"number\").getValue());\n        }\n\n        @Override\n        public boolean apply(Facts facts) {\n            System.out.println(name + \": \" + facts.getFact(\"number\").getValue());\n            return true;\n        }\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/RuleBuilderTest.java",
    "content": "package com.alibaba.cola.ruleengine;\n\nimport com.alibaba.cola.ruleengine.api.Facts;\nimport com.alibaba.cola.ruleengine.api.Rule;\nimport com.alibaba.cola.ruleengine.api.RuleEngine;\nimport com.alibaba.cola.ruleengine.core.*;\nimport org.junit.jupiter.api.Test;\n\npublic class RuleBuilderTest {\n\n    @Test\n    public void testFizzBuzz() {\n        RuleEngine fizzBuzzEngine = new DefaultRuleEngine();\n\n        // create rules\n        Rule fizzRule = new RuleBuilder()\n                .name(\"fizzRule\")\n                .description(\"fizz rule when input times 3, output is Fizz\")\n                .priority(10)\n                .when(facts -> (int) facts.get(\"number\") % 3 == 0)\n                .then(facts -> System.out.print(\"Fizz\"))\n                .build();\n\n        Rule buzzRule = new RuleBuilder()\n                .name(\"buzzRule\")\n                .description(\"buzz rule when input times 5, output is buzz\")\n                .priority(1)\n                .when(facts -> (int) facts.get(\"number\") % 5 == 0)\n                .then(facts -> System.out.print(\"Buzz\"))\n                .build();\n\n\n        Rule fizzBuzzRule = AllRules.allOf(fizzRule, buzzRule)\n                .name(\"fizzBuzzRule\")\n                .priority(30);\n\n        Rule defaultRule = new RuleBuilder()\n                .name(\"defaultRule\")\n                .description(\"default rule, output number\")\n                .priority(40)\n                .when(facts -> true)\n                .then(facts -> System.out.print((int) facts.get(\"number\")))\n                .build();\n\n        Rule rule = AnyRules.anyOf(fizzBuzzRule, fizzRule, buzzRule, defaultRule)\n                .name(\"anyRule\");\n\n        // fire rules\n        Facts facts = new Facts();\n        facts.put(\"number\", 15);\n        fizzBuzzEngine.fire(rule, facts);\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/RuleEngineTest.java",
    "content": "package com.alibaba.cola.ruleengine;\n\nimport com.alibaba.cola.ruleengine.api.Facts;\nimport com.alibaba.cola.ruleengine.api.Rule;\nimport com.alibaba.cola.ruleengine.api.RuleEngine;\nimport com.alibaba.cola.ruleengine.core.*;\nimport org.junit.jupiter.api.Test;\n\npublic class RuleEngineTest {\n\n    @Test\n    public void testRuleEngine() {\n        RuleEngine ruleEngine = new DefaultRuleEngine();\n        ruleEngine.fire(null, null);\n    }\n\n    @Test\n    public void testFizzBuzz() {\n        RuleEngine fizzBuzzEngine = new DefaultRuleEngine();\n\n        // create rules\n        Rule fizzRule = new DefaultRule(facts -> (int) facts.get(\"number\") % 3 == 0, facts -> System.out.print(\"Fizz\"));\n        Rule buzzRule = new DefaultRule(facts -> (int) facts.get(\"number\") % 5 == 0, facts -> System.out.print(\"Buzz\"));\n        Rule fizzBuzzRule = AllRules.allOf(fizzRule, buzzRule);\n        Rule NonFizzBuzzRule = new DefaultRule(facts -> true, facts -> System.out.print((int) facts.get(\"number\")));\n        Rule rule = AnyRules.anyOf(fizzBuzzRule, fizzRule, buzzRule, NonFizzBuzzRule);\n\n        // fire rules\n        Facts facts = new Facts();\n        facts.put(\"number\", 15);\n        fizzBuzzEngine.fire(rule, facts);\n//        for (int i = 1; i <= 10; i++) {\n//            facts.put(\"number\", i);\n//            fizzBuzzEngine.fire(rules, facts);\n//            System.out.println();\n//        }\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/fizzbuzz/FizzBuzzTest.java",
    "content": "package com.alibaba.cola.ruleengine.fizzbuzz;\n\n//import com.alibaba.cola.ruleengine.fizzbuzz.v1.FizzBuzz;\nimport com.alibaba.cola.ruleengine.fizzbuzz.v2.FizzBuzz;\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\n\npublic class FizzBuzzTest {\n\n    @Test\n    public void num_given_1() {\n        //given\n        int input = 1;\n        //when\n        String result = FizzBuzz.count(input);\n        //then\n        assertEquals(result, \"1\");\n    }\n\n    @Test\n    public void fizz_given_3() {\n        //given\n        int input = 3;\n        //when\n        String result = FizzBuzz.count(input);\n        //then\n        assertEquals(result, \"Fizz\");\n    }\n\n    @Test\n    public void buzz_given_5() {\n        //given\n        int input = 5;\n        //when\n        String result = FizzBuzz.count(input);\n        //then\n        assertEquals(result, \"Buzz\");\n    }\n\n    @Test\n    public void fizz_buzz_given_15() {\n        //given\n        int input = 15;\n        //when\n        String result = FizzBuzz.count(input);\n        //then\n        assertEquals(result, \"FizzBuzz\");\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/fizzbuzz/v1/FizzBuzz.java",
    "content": "package com.alibaba.cola.ruleengine.fizzbuzz.v1;\n\npublic class FizzBuzz {\n    public static String count(int n){\n        if (((n % 3) == 0) && ((n % 5) == 0))\n            return \"FizzBuzz\";\n        if ((n % 3) == 0)\n            return \"Fizz\";\n        if ((n % 5) == 0)\n            return \"Buzz\";\n        return String.valueOf(n);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/fizzbuzz/v2/Action.java",
    "content": "package com.alibaba.cola.ruleengine.fizzbuzz.v2;\n\n@FunctionalInterface\npublic interface Action {\n    String execute(int n);\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/fizzbuzz/v2/Condition.java",
    "content": "package com.alibaba.cola.ruleengine.fizzbuzz.v2;\n\nimport java.util.Objects;\n\n@FunctionalInterface\npublic interface Condition {\n\n    boolean evaluate(int n);\n\n    //谓词and逻辑，参考Predicate\n    default Condition and(Condition other) {\n        Objects.requireNonNull(other);\n        return (n) -> {\n            return this.evaluate(n) && other.evaluate(n);\n        };\n    }\n\n    //谓词or逻辑，参考Predicate\n    default Condition or(Condition other) {\n        Objects.requireNonNull(other);\n        return (n) -> {\n            return this.evaluate(n) || other.evaluate(n);\n        };\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/fizzbuzz/v2/FizzBuzz.java",
    "content": "package com.alibaba.cola.ruleengine.fizzbuzz.v2;\n\nimport static com.alibaba.cola.ruleengine.fizzbuzz.v2.SimpleRuleEngine.*;\nimport static com.alibaba.cola.ruleengine.fizzbuzz.v2.TimesCondition.times;\n\n/**\n * 用简易规则引擎重构后的FizzBuzz实现\n */\npublic class FizzBuzz {\n    public static String count(int i){\n        //Composite condition\n        Rule fizzBuzzRule = atom(times(3).and(times(5)), n -> \"FizzBuzz\");\n        Rule fizzRule = atom(times(3) , n -> \"Fizz\");\n        Rule buzzRule = atom(times(5), n -> \"Buzz\");\n        //Composite rule\n        Rule compositeFizzBuzzRule = allOf(fizzRule, buzzRule);\n        Rule defaultRule = atom(n -> true, n -> String.valueOf(n));\n        Rule rule = anyOf(compositeFizzBuzzRule, fizzRule, buzzRule, defaultRule);\n        return rule.apply(i);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/fizzbuzz/v2/Rule.java",
    "content": "package com.alibaba.cola.ruleengine.fizzbuzz.v2;\n\n@FunctionalInterface\npublic interface Rule {\n    String apply(int n);\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/fizzbuzz/v2/SimpleRuleEngine.java",
    "content": "package com.alibaba.cola.ruleengine.fizzbuzz.v2;\n\nimport java.util.Arrays;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * 通过原子atom rule，以及atom rule之间的组合解决FizzBuzz问题\n * 这里为了简单使用Rule的组合模式代替了RuleEngine实体\n * 注意：这个SimpleRuleEngine只能解决输入为n，输出为String的FizzBuzz问题\n * 完全不具备通用性\n */\npublic class SimpleRuleEngine {\n    public static Rule atom(Condition condition, Action action){\n        return n -> condition.evaluate(n) ? action.execute(n) : \"\";\n    }\n\n    public static Rule anyOf(Rule... rules){\n        return n -> stringStream(n, rules).filter(s -> !s.isEmpty()).findFirst().get();\n    }\n\n    public static Rule allOf(Rule... rules){\n        return n -> stringStream(n, rules).collect(Collectors.joining());\n    }\n\n    public static Stream<String> stringStream(int n, Rule[] rules){\n        return Arrays.stream(rules).map(r -> r.apply(n));\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/java/com/alibaba/cola/ruleengine/fizzbuzz/v2/TimesCondition.java",
    "content": "package com.alibaba.cola.ruleengine.fizzbuzz.v2;\n\n/**\n * 计算倍数关系的谓词逻辑\n */\npublic class TimesCondition {\n    public static Condition times(int i){\n        return n -> n % i == 0;\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-ruleengine/src/test/resources/logback-test.xml",
    "content": "<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <!--定义了两个appender，日志会通过往这两个appender里面写-->\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n\n    <!--应用日志-->\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n    <logger name=\"com.alibaba.cola.ruleengine\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/.gitignore",
    "content": "target/\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/README.md",
    "content": "## 作用\n简单、轻量、性能极高的状态机DSL实现，解决业务中的状态流转问题。\n\n## 原理\nhttps://blog.csdn.net/significantfrank/article/details/104996419\n\n\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <dependencies>\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter</artifactId>\n            <version>RELEASE</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n    <parent>\n        <groupId>com.alibaba.cola</groupId>\n        <artifactId>cola-components-parent</artifactId>\n        <version>5.x-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>cola-component-statemachine</artifactId>\n    <packaging>jar</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n</project>\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/Action.java",
    "content": "package com.alibaba.cola.statemachine;\n\n/**\n * Generic strategy interface used by a state machine to respond\n * events by executing an {@code Action} with a {@link StateContext}.\n *\n * @author Frank Zhang\n * @date 2020-02-07 2:51 PM\n */\npublic interface Action<S, E, C> {\n\n//    /**\n//     * Execute action with a {@link StateContext}.\n//     *\n//     * @param context the state context\n//     */\n//    void execute(StateContext<S, E> context);\n\n    public void execute(S from, S to, E event, C context);\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/Condition.java",
    "content": "package com.alibaba.cola.statemachine;\n\n/**\n * Condition\n *\n * @author Frank Zhang\n * @date 2020-02-07 2:50 PM\n */\npublic interface Condition<C> {\n\n    /**\n     * @param context context object\n     * @return whether the context satisfied current condition\n     */\n    boolean isSatisfied(C context);\n\n    default String name(){\n        return this.getClass().getSimpleName();\n    }\n}"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/State.java",
    "content": "package com.alibaba.cola.statemachine;\n\nimport com.alibaba.cola.statemachine.impl.TransitionType;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * State\n *\n * @param <S> the type of state\n * @param <E> the type of event\n *\n * @author Frank Zhang\n * @date 2020-02-07 2:12 PM\n */\npublic interface State<S,E,C> extends Visitable{\n\n    /**\n     * Gets the state identifier.\n     *\n     * @return the state identifiers\n     */\n    S getId();\n\n    /**\n     * Add transition to the state\n     * @param event the event of the Transition\n     * @param target the target of the transition\n     * @return\n     */\n    Transition<S,E,C> addTransition(E event, State<S, E, C> target, TransitionType transitionType);\n\n    List<Transition<S,E,C>> addTransitions(E event, List<State<S, E, C>> targets, TransitionType transitionType);\n\n    List<Transition<S,E,C>> getEventTransitions(E event);\n\n    Collection<Transition<S,E,C>> getAllTransitions();\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/StateContext.java",
    "content": "package com.alibaba.cola.statemachine;\n\n/**\n * StateContext\n *\n * @author Frank Zhang\n * @date 2020-02-07 2:49 PM\n */\npublic interface StateContext<S, E, C> {\n    /**\n     * Gets the transition.\n     *\n     * @return the transition\n     */\n    Transition<S, E, C> getTransition();\n\n    /**\n     * Gets the state machine.\n     *\n     * @return the state machine\n     */\n    StateMachine<S, E, C> getStateMachine();\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/StateMachine.java",
    "content": "package com.alibaba.cola.statemachine;\n\nimport java.util.List;\n\n/**\n * StateMachine\n *\n * @author Frank Zhang\n *\n * @param <S> the type of state\n * @param <E> the type of event\n * @param <C> the user defined context\n * @date 2020-02-07 2:13 PM\n */\npublic interface StateMachine<S, E, C> extends Visitable{\n\n    /**\n     * Verify if an event {@code E} can be fired from current state {@code S}\n     * @param sourceStateId\n     * @param event\n     * @return\n     */\n    boolean verify(S sourceStateId,E event);\n\n    /**\n     * Send an event {@code E} to the state machine.\n     *\n     * @param sourceState the source state\n     * @param event the event to send\n     * @param ctx the user defined context\n     * @return the target state\n     */\n     S fireEvent(S sourceState, E event, C ctx);\n\n     List<S> fireParallelEvent(S sourceState, E event, C ctx);\n\n    /**\n     * MachineId is the identifier for a State Machine\n     * @return\n     */\n    String getMachineId();\n\n    /**\n     * Use visitor pattern to display the structure of the state machine\n     */\n    void showStateMachine();\n\n    String generatePlantUML();\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/StateMachineFactory.java",
    "content": "package com.alibaba.cola.statemachine;\n\nimport com.alibaba.cola.statemachine.impl.StateMachineException;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * StateMachineFactory\n *\n * @author Frank Zhang\n * @date 2020-02-08 10:21 PM\n */\npublic class StateMachineFactory {\n    static Map<String /* machineId */, StateMachine> stateMachineMap = new ConcurrentHashMap<>();\n\n    public static <S, E, C> void register(StateMachine<S, E, C> stateMachine){\n        String machineId = stateMachine.getMachineId();\n        if(stateMachineMap.get(machineId) != null){\n            throw new StateMachineException(\"The state machine with id [\"+machineId+\"] is already built, no need to build again\");\n        }\n        stateMachineMap.put(stateMachine.getMachineId(), stateMachine);\n    }\n\n    public static <S, E, C> StateMachine<S, E, C> get(String machineId){\n        StateMachine stateMachine = stateMachineMap.get(machineId);\n        if(stateMachine == null){\n            throw new StateMachineException(\"There is no stateMachine instance for \"+machineId+\", please build it first\");\n        }\n        return stateMachine;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/Transition.java",
    "content": "package com.alibaba.cola.statemachine;\n\nimport com.alibaba.cola.statemachine.impl.TransitionType;\n\n/**\n * {@code Transition} is something what a state machine associates with a state\n * changes.\n *\n * @author Frank Zhang\n *\n * @param <S> the type of state\n * @param <E> the type of event\n * @param <C> the type of user defined context, which is used to hold application data\n *\n * @date 2020-02-07 2:20 PM\n */\npublic interface Transition<S, E, C>{\n    /**\n     * Gets the source state of this transition.\n     *\n     * @return the source state\n     */\n    State<S,E,C> getSource();\n\n    void setSource(State<S, E, C> state);\n\n    E getEvent();\n\n    void setEvent(E event);\n\n    void setType(TransitionType type);\n    /**\n     * Gets the target state of this transition.\n     *\n     * @return the target state\n     */\n    State<S,E,C> getTarget();\n\n    void setTarget(State<S, E, C> state);\n\n    /**\n     * Gets the guard of this transition.\n     *\n     * @return the guard\n     */\n    Condition<C> getCondition();\n\n    void setCondition(Condition<C> condition);\n\n    Action<S,E,C> getAction();\n\n    void setAction(Action<S, E, C> action);\n\n    /**\n     * Do transition from source state to target state.\n     *\n     * @return the target state\n     */\n\n    State<S, E, C> transit(C ctx, boolean checkCondition);\n    /**\n     * Verify transition correctness\n     */\n    void verify();\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/Visitable.java",
    "content": "package com.alibaba.cola.statemachine;\n\n/**\n * Visitable\n *\n * @author Frank Zhang\n * @date 2020-02-08 8:41 PM\n */\npublic interface Visitable {\n    String accept(final Visitor visitor);\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/Visitor.java",
    "content": "package com.alibaba.cola.statemachine;\n\n/**\n * Visitor\n *\n * @author Frank Zhang\n * @date 2020-02-08 8:41 PM\n */\npublic interface Visitor {\n\n    char LF = '\\n';\n\n    /**\n     * @param visitable the element to be visited.\n     * @return\n     */\n    String visitOnEntry(StateMachine<?, ?, ?> visitable);\n\n    /**\n     * @param visitable the element to be visited.\n     * @return\n     */\n    String visitOnExit(StateMachine<?, ?, ?> visitable);\n\n    /**\n     * @param visitable the element to be visited.\n     * @return\n     */\n    String visitOnEntry(State<?, ?, ?> visitable);\n\n    /**\n     * @param visitable the element to be visited.\n     * @return\n     */\n    String visitOnExit(State<?, ?, ?> visitable);\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/AbstractParallelTransitionBuilder.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\nimport com.alibaba.cola.statemachine.State;\nimport com.alibaba.cola.statemachine.impl.StateHelper;\nimport com.alibaba.cola.statemachine.impl.TransitionType;\n\nimport java.util.List;\nimport java.util.Map;\n\n abstract class AbstractParallelTransitionBuilder<S,E,C> implements  ParallelFrom<S,E,C>,On<S,E,C>,To<S,E,C>{\n\n    final Map<S, State<S, E, C>> stateMap;\n\n    protected List<State<S, E, C>> targets;\n\n    final TransitionType transitionType;\n\n    public AbstractParallelTransitionBuilder(Map<S, State<S, E, C>> stateMap, TransitionType transitionType) {\n        this.stateMap = stateMap;\n        this.transitionType = transitionType;\n    }\n    @Override\n    public To<S, E, C> toAmong(S ... stateIds) {\n        targets = StateHelper.getStates(stateMap, stateIds);\n        return this;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/AbstractTransitionBuilder.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\nimport com.alibaba.cola.statemachine.State;\nimport com.alibaba.cola.statemachine.impl.StateHelper;\nimport com.alibaba.cola.statemachine.impl.TransitionType;\n\nimport java.util.Map;\n\n/**\n * Take TransitionBuilderImpl and TransitionsBuilderImpl sharing variables\n * and methods to that abstract class, which acts as their parent,\n * instead of having TransitionsBuilderImpl inherit from\n * TransitionsBuilderImpl. I think that the multi-flow\n * builder(TransitionsBuilderImpl) and single-flow\n * builder(TransitionBuilderImpl) are equal and not supposed to be\n * parent-child relationship, they from, when, and perform methods\n * are not the same, and although it looks like just a set of loops\n * but logically should not be inherited over Override.\n * ( Just as there was no relationship, why should we talk to each other,\n * say a we are not suitable). With the abstract class, multi-flow and single-flow\n * only use to focus on their respective functions are single-flow,\n * or multi-flow. Conform to a single duty.\n * @author welliem\n * @date 2023-07-14 12:13\n */\n abstract class AbstractTransitionBuilder<S,E,C> implements  From<S,E,C>,On<S,E,C>,To<S,E,C>{\n\n    final Map<S, State<S, E, C>> stateMap;\n\n    protected State<S, E, C> target;\n\n    final TransitionType transitionType;\n\n    public AbstractTransitionBuilder(Map<S, State<S, E, C>> stateMap, TransitionType transitionType) {\n        this.stateMap = stateMap;\n        this.transitionType = transitionType;\n    }\n    @Override\n    public To<S, E, C> to(S stateId) {\n        target = StateHelper.getState(stateMap, stateId);\n        return this;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/AlertFailCallback.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\nimport com.alibaba.cola.statemachine.exception.TransitionFailException;\n\n/**\n * Alert fail callback, throw an {@code TransitionFailException}\n *\n * @author 龙也\n * @date 2022/9/15 12:02 PM\n */\npublic class AlertFailCallback<S, E, C> implements FailCallback<S, E, C> {\n\n    @Override\n    public void onFail(S sourceState, E event, C context) {\n        throw new TransitionFailException(\n            \"Cannot fire event [\" + event + \"] on current state [\" + sourceState + \"] with context [\" + context + \"]\"\n        );\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/ExternalParallelTransitionBuilder.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\n\npublic interface ExternalParallelTransitionBuilder<S, E, C> {\n    /**\n     * Build transition source state.\n     * @param stateId id of state\n     * @return from clause builder\n     */\n    ParallelFrom<S, E, C> from(S stateId);\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/ExternalTransitionBuilder.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\n/**\n * ExternalTransitionBuilder\n *\n * @author Frank Zhang\n * @date 2020-02-07 6:11 PM\n */\npublic interface ExternalTransitionBuilder<S, E, C> {\n    /**\n     * Build transition source state.\n     * @param stateId id of state\n     * @return from clause builder\n     */\n    From<S, E, C> from(S stateId);\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/ExternalTransitionsBuilder.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\n/**\n * ExternalTransitionsBuilder\n *\n * This builder is for multiple transitions, currently only support multiple sources <----> one target\n *\n * @author Frank Zhang\n * @date 2020-02-08 7:41 PM\n */\npublic interface ExternalTransitionsBuilder<S, E, C> {\n    From<S, E, C> fromAmong(S... stateIds);\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/FailCallback.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\n/**\n * FailCallback\n *\n * @author 龙也\n * @date 2022/9/15 12:02 PM\n */\n@FunctionalInterface\npublic interface FailCallback<S, E, C> {\n\n    /**\n     * Callback function to execute if failed to trigger an Event\n     *\n     * @param sourceState\n     * @param event\n     * @param context\n     */\n    void onFail(S sourceState, E event, C context);\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/From.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\n/**\n * From\n *\n * @author Frank Zhang\n * @date 2020-02-07 6:13 PM\n */\npublic interface From<S, E, C> {\n    /**\n     * Build transition target state and return to clause builder\n     * @param stateId id of state\n     * @return To clause builder\n     */\n    To<S, E, C> to(S stateId);\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/InternalTransitionBuilder.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\n/**\n * InternalTransitionBuilder\n *\n * @author Frank Zhang\n * @date 2020-02-07 9:39 PM\n */\npublic interface InternalTransitionBuilder <S, E, C> {\n    /**\n     * Build a internal transition\n     * @param stateId id of transition\n     * @return To clause builder\n     */\n    To<S, E, C> within(S stateId);\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/NumbFailCallback.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\n/**\n * Default fail callback, do nothing.\n *\n * @author 龙也\n * @date 2022/9/15 12:02 PM\n */\npublic class NumbFailCallback<S, E, C> implements FailCallback<S, E, C> {\n\n    @Override\n    public void onFail(S sourceState, E event, C context) {\n        //do nothing\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/On.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\nimport com.alibaba.cola.statemachine.Condition;\n\n/**\n * On\n *\n * @author Frank Zhang\n * @date 2020-02-07 6:14 PM\n */\npublic interface On<S, E, C> extends When<S, E, C>{\n    /**\n     * Add condition for the transition\n     * @param condition transition condition\n     * @return When clause builder\n     */\n    When<S, E, C> when(Condition<C> condition);\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/ParallelFrom.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\n\npublic interface ParallelFrom<S, E, C> {\n    /**\n     * Build transition target state and return to clause builder\n     * @param stateIds id of state\n     * @return To clause builder\n     */\n    To<S, E, C> toAmong(S ... stateIds);\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/ParallelTransitionBuilderImpl.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\nimport com.alibaba.cola.statemachine.Action;\nimport com.alibaba.cola.statemachine.Condition;\nimport com.alibaba.cola.statemachine.State;\nimport com.alibaba.cola.statemachine.Transition;\nimport com.alibaba.cola.statemachine.impl.StateHelper;\nimport com.alibaba.cola.statemachine.impl.TransitionType;\n\nimport java.util.List;\nimport java.util.Map;\n\nclass ParallelTransitionBuilderImpl<S,E,C> extends AbstractParallelTransitionBuilder<S,E,C> implements ExternalParallelTransitionBuilder<S,E,C> {\n\n\n    private State<S, E, C> source;\n    private List<Transition<S, E, C>> transitions;\n\n    public ParallelTransitionBuilderImpl(Map<S, State<S, E, C>> stateMap, TransitionType transitionType) {\n        super(stateMap, transitionType);\n    }\n\n    @Override\n    public ParallelFrom<S, E, C> from(S stateId) {\n        source = StateHelper.getState(stateMap, stateId);\n        return this;\n    }\n\n    @Override\n    public When<S, E, C> when(Condition<C> condition) {\n        for (Transition<S, E, C> transition : transitions) {\n            transition.setCondition(condition);\n        }\n        return this;\n    }\n\n    @Override\n    public On<S, E, C> on(E event) {\n        transitions = source.addTransitions(event, targets, transitionType);\n        return this;\n    }\n\n    @Override\n    public void perform(Action<S, E, C> action) {\n        for (Transition<S, E, C> transition : transitions) {\n            transition.setAction(action);\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/StateMachineBuilder.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\nimport com.alibaba.cola.statemachine.StateMachine;\n\n/**\n * StateMachineBuilder\n *\n * @author Frank Zhang\n * @date 2020-02-07 5:32 PM\n */\npublic interface StateMachineBuilder<S, E, C> {\n    /**\n     * Builder for one transition\n     *\n     * @return External transition builder\n     */\n    ExternalTransitionBuilder<S, E, C> externalTransition();\n\n    /**\n     * Builder for multiple transitions\n     *\n     * @return External transition builder\n     */\n    ExternalTransitionsBuilder<S, E, C> externalTransitions();\n    /**\n     * Builder for parallel transitions\n     *\n     * @return External transition builder\n     */\n    ExternalParallelTransitionBuilder<S, E, C> externalParallelTransition();\n\n    /**\n     * Start to build internal transition\n     *\n     * @return Internal transition builder\n     */\n    InternalTransitionBuilder<S, E, C> internalTransition();\n\n    /**\n     * set up fail callback, default do nothing {@code NumbFailCallbackImpl}\n     *\n     * @param callback\n     */\n    void setFailCallback(FailCallback<S, E, C> callback);\n\n    StateMachine<S, E, C> build(String machineId);\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/StateMachineBuilderFactory.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\n/**\n * StateMachineBuilderFactory\n *\n * @author Frank Zhang\n * @date 2020-02-08 12:33 PM\n */\npublic class StateMachineBuilderFactory {\n    public static <S, E, C> StateMachineBuilder<S, E, C> create(){\n        return new StateMachineBuilderImpl<>();\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/StateMachineBuilderImpl.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.alibaba.cola.statemachine.State;\nimport com.alibaba.cola.statemachine.StateMachine;\nimport com.alibaba.cola.statemachine.StateMachineFactory;\nimport com.alibaba.cola.statemachine.impl.StateMachineImpl;\nimport com.alibaba.cola.statemachine.impl.TransitionType;\n\n/**\n * StateMachineBuilderImpl\n *\n * @author Frank Zhang\n * @date 2020-02-07 9:40 PM\n */\npublic class StateMachineBuilderImpl<S, E, C> implements StateMachineBuilder<S, E, C> {\n\n    /**\n     * StateMap is the same with stateMachine, as the core of state machine is holding reference to states.\n     */\n    private final Map<S, State<S, E, C>> stateMap = new ConcurrentHashMap<>();\n    private final StateMachineImpl<S, E, C> stateMachine = new StateMachineImpl<>(stateMap);\n    private FailCallback<S, E, C> failCallback = new NumbFailCallback<>();\n\n    @Override\n    public ExternalTransitionBuilder<S, E, C> externalTransition() {\n        return new TransitionBuilderImpl<>(stateMap, TransitionType.EXTERNAL);\n    }\n\n    @Override\n    public ExternalTransitionsBuilder<S, E, C> externalTransitions() {\n        return new TransitionsBuilderImpl<>(stateMap, TransitionType.EXTERNAL);\n    }\n\n    @Override\n    public ExternalParallelTransitionBuilder<S, E, C> externalParallelTransition() {\n        return new ParallelTransitionBuilderImpl<>(stateMap, TransitionType.EXTERNAL);\n    }\n\n    @Override\n    public InternalTransitionBuilder<S, E, C> internalTransition() {\n        return new TransitionBuilderImpl<>(stateMap, TransitionType.INTERNAL);\n    }\n\n    @Override\n    public void setFailCallback(FailCallback<S, E, C> callback) {\n        this.failCallback = callback;\n    }\n\n    @Override\n    public StateMachine<S, E, C> build(String machineId) {\n        stateMachine.setMachineId(machineId);\n        stateMachine.setReady(true);\n        stateMachine.setFailCallback(failCallback);\n        StateMachineFactory.register(stateMachine);\n        return stateMachine;\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/To.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\n/**\n * To\n *\n * @author Frank Zhang\n * @date 2020-02-07 6:14 PM\n */\npublic interface To<S, E, C> {\n    /**\n     * Build transition event\n     * @param event transition event\n     * @return On clause builder\n     */\n    On<S, E, C> on(E event);\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/TransitionBuilderImpl.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\nimport com.alibaba.cola.statemachine.Action;\nimport com.alibaba.cola.statemachine.Condition;\nimport com.alibaba.cola.statemachine.State;\nimport com.alibaba.cola.statemachine.Transition;\nimport com.alibaba.cola.statemachine.impl.StateHelper;\nimport com.alibaba.cola.statemachine.impl.TransitionType;\n\nimport java.util.Map;\n\n/**\n * TransitionBuilderImpl\n *\n * @author Frank Zhang\n * @date 2020-02-07 10:20 PM\n */\nclass TransitionBuilderImpl<S,E,C> extends AbstractTransitionBuilder<S,E,C> implements ExternalTransitionBuilder<S,E,C>, InternalTransitionBuilder<S,E,C> {\n\n\n    private State<S, E, C> source;\n    private Transition<S, E, C> transition;\n\n    public TransitionBuilderImpl(Map<S, State<S, E, C>> stateMap, TransitionType transitionType) {\n        super(stateMap, transitionType);\n    }\n\n    @Override\n    public From<S, E, C> from(S stateId) {\n        source = StateHelper.getState(stateMap, stateId);\n        return this;\n    }\n\n    @Override\n    public To<S, E, C> within(S stateId) {\n        source = target = StateHelper.getState(stateMap, stateId);\n        return this;\n    }\n\n    @Override\n    public When<S, E, C> when(Condition<C> condition) {\n        transition.setCondition(condition);\n        return this;\n    }\n\n    @Override\n    public On<S, E, C> on(E event) {\n        transition = source.addTransition(event, target, transitionType);\n        return this;\n    }\n\n    @Override\n    public void perform(Action<S, E, C> action) {\n        transition.setAction(action);\n    }\n\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/TransitionsBuilderImpl.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\nimport com.alibaba.cola.statemachine.Action;\nimport com.alibaba.cola.statemachine.Condition;\nimport com.alibaba.cola.statemachine.State;\nimport com.alibaba.cola.statemachine.Transition;\nimport com.alibaba.cola.statemachine.impl.StateHelper;\nimport com.alibaba.cola.statemachine.impl.TransitionType;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * TransitionsBuilderImpl\n *\n * @author Frank Zhang\n * @date 2020-02-08 7:43 PM\n */\npublic class TransitionsBuilderImpl<S,E,C> extends AbstractTransitionBuilder<S,E,C> implements ExternalTransitionsBuilder<S,E,C> {\n    /**\n     * This is for fromAmong where multiple sources can be configured to point to one target\n     */\n    private List<State<S, E, C>> sources = new ArrayList<>();\n\n    private List<Transition<S, E, C>> transitions = new ArrayList<>();\n\n    public TransitionsBuilderImpl(Map<S, State<S, E, C>> stateMap, TransitionType transitionType) {\n        super(stateMap, transitionType);\n    }\n\n    @Override\n    public From<S, E, C> fromAmong(S... stateIds) {\n        for(S stateId : stateIds) {\n            sources.add(StateHelper.getState(super.stateMap, stateId));\n        }\n        return this;\n    }\n\n    @Override\n    public On<S, E, C> on(E event) {\n        for(State source : sources) {\n            Transition transition = source.addTransition(event, super.target, super.transitionType);\n            transitions.add(transition);\n        }\n        return this;\n    }\n\n    @Override\n    public When<S, E, C> when(Condition<C> condition) {\n        for(Transition transition : transitions){\n            transition.setCondition(condition);\n        }\n        return this;\n    }\n\n    @Override\n    public void perform(Action<S, E, C> action) {\n        for(Transition transition : transitions){\n            transition.setAction(action);\n        }\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/When.java",
    "content": "package com.alibaba.cola.statemachine.builder;\n\nimport com.alibaba.cola.statemachine.Action;\n\n/**\n * When\n *\n * @author Frank Zhang\n * @date 2020-02-07 9:33 PM\n */\npublic interface When<S, E, C>{\n    /**\n     * Define action to be performed during transition\n     *\n     * @param action performed action\n     */\n    void perform(Action<S, E, C> action);\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/builder/package-info.java",
    "content": "/**\n * The builder is to provide fluent interfaces for statemachine, which is a classic Internal DSL implementing skill.\n *\n * For more information, please check Martin Fowler's Article: <a>https://martinfowler.com/bliki/FluentInterface.html</a>\n * @author Frank Zhang\n */\npackage com.alibaba.cola.statemachine.builder;"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/exception/TransitionFailException.java",
    "content": "package com.alibaba.cola.statemachine.exception;\n\n/**\n * @author 龙也\n * @date 2022/9/15 12:08 PM\n */\npublic class TransitionFailException extends RuntimeException {\n\n    public TransitionFailException(String errMsg) {\n        super(errMsg);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/Debugger.java",
    "content": "package com.alibaba.cola.statemachine.impl;\n\n/**\n * Debugger, This is used to decouple Logging framework dependency\n *\n * @author Frank Zhang\n * @date 2020-02-11 11:08 AM\n */\npublic class Debugger {\n\n    private static boolean isDebugOn = false;\n\n    public static void debug(String message){\n        if(isDebugOn){\n            System.out.println(message);\n        }\n    }\n\n    public static void enableDebug(){\n        isDebugOn = true;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/EventTransitions.java",
    "content": "package com.alibaba.cola.statemachine.impl;\n\nimport com.alibaba.cola.statemachine.Transition;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\n/**\n * EventTransitions\n *\n * 同一个Event可以触发多个Transitions，https://github.com/alibaba/COLA/pull/158\n *\n * @author Frank Zhang\n * @date 2021-05-28 5:17 PM\n */\npublic class EventTransitions<S,E,C> {\n    private HashMap<E, List<Transition<S,E,C>>> eventTransitions;\n\n    public EventTransitions(){\n        eventTransitions = new HashMap<>();\n    }\n\n    public void put(E event, Transition<S, E, C> transition){\n        if(eventTransitions.get(event) == null){\n            List<Transition<S,E,C>> transitions = new ArrayList<>();\n            transitions.add(transition);\n            eventTransitions.put(event, transitions);\n        }\n        else{\n            List existingTransitions = eventTransitions.get(event);\n            verify(existingTransitions, transition);\n            existingTransitions.add(transition);\n        }\n    }\n\n    /**\n     * Per one source and target state, there is only one transition is allowed\n     * @param existingTransitions\n     * @param newTransition\n     */\n    private void verify(List<Transition<S,E,C>> existingTransitions, Transition<S,E,C> newTransition) {\n        for (Transition transition : existingTransitions) {\n            if (transition.equals(newTransition)) {\n                throw new StateMachineException(transition + \" already Exist, you can not add another one\");\n            }\n        }\n    }\n\n    public List<Transition<S,E,C>> get(E event){\n        return eventTransitions.get(event);\n    }\n\n    public List<Transition<S,E,C>> allTransitions(){\n        List<Transition<S,E,C>> allTransitions = new ArrayList<>();\n        for(List<Transition<S,E,C>> transitions : eventTransitions.values()){\n            allTransitions.addAll(transitions);\n        }\n        return allTransitions;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/PlantUMLVisitor.java",
    "content": "package com.alibaba.cola.statemachine.impl;\n\nimport com.alibaba.cola.statemachine.State;\nimport com.alibaba.cola.statemachine.StateMachine;\nimport com.alibaba.cola.statemachine.Transition;\nimport com.alibaba.cola.statemachine.Visitor;\n\n/**\n * PlantUMLVisitor\n *\n * @author Frank Zhang\n * @date 2020-02-09 7:47 PM\n */\npublic class PlantUMLVisitor implements Visitor {\n\n    /**\n     * Since the state machine is stateless, there is no initial state.\n     *\n     * You have to add \"[*] -> initialState\" to mark it as a state machine diagram.\n     * otherwise it will be recognized as a sequence diagram.\n     *\n     * @param visitable the element to be visited.\n     * @return\n     */\n    @Override\n    public String visitOnEntry(StateMachine<?, ?, ?> visitable) {\n        return \"@startuml\" + LF;\n    }\n\n    @Override\n    public String visitOnExit(StateMachine<?, ?, ?> visitable) {\n        return \"@enduml\";\n    }\n\n    @Override\n    public String visitOnEntry(State<?, ?, ?> state) {\n        StringBuilder sb = new StringBuilder();\n        for(Transition transition: state.getAllTransitions()){\n            sb.append(transition.getSource().getId())\n                    .append(\" --> \")\n                    .append(transition.getTarget().getId())\n                    .append(\" : \")\n                    .append(transition.getEvent())\n                    .append(LF);\n        }\n        return sb.toString();\n    }\n\n    @Override\n    public String visitOnExit(State<?, ?, ?> state) {\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/StateHelper.java",
    "content": "package com.alibaba.cola.statemachine.impl;\n\nimport com.alibaba.cola.statemachine.State;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * StateHelper\n *\n * @author Frank Zhang\n * @date 2020-02-08 4:23 PM\n */\npublic class StateHelper {\n    public static <S, E, C> State<S, E, C> getState(Map<S, State<S, E, C>> stateMap, S stateId){\n        State<S, E, C> state = stateMap.get(stateId);\n        if (state == null) {\n            state = new StateImpl<>(stateId);\n            stateMap.put(stateId, state);\n        }\n        return state;\n    }\n    public static <C, E, S> List<State<S,E,C>> getStates(Map<S, State<S,E,C>> stateMap, S ... stateIds) {\n        List<State<S, E, C>> result = new ArrayList<>();\n        for (S stateId : stateIds) {\n            State<S, E, C> state = getState(stateMap, stateId);\n            result.add(state);\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/StateImpl.java",
    "content": "package com.alibaba.cola.statemachine.impl;\n\nimport com.alibaba.cola.statemachine.State;\nimport com.alibaba.cola.statemachine.Transition;\nimport com.alibaba.cola.statemachine.Visitor;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * StateImpl\n *\n * @author Frank Zhang\n * @date 2020-02-07 11:19 PM\n */\npublic class StateImpl<S,E,C> implements State<S,E,C> {\n    protected final S stateId;\n    private EventTransitions eventTransitions = new EventTransitions();\n\n    StateImpl(S stateId){\n        this.stateId = stateId;\n    }\n\n    @Override\n    public Transition<S, E, C> addTransition(E event, State<S,E,C> target, TransitionType transitionType) {\n        Transition<S, E, C> newTransition = new TransitionImpl<>();\n        newTransition.setSource(this);\n        newTransition.setTarget(target);\n        newTransition.setEvent(event);\n        newTransition.setType(transitionType);\n\n        Debugger.debug(\"Begin to add new transition: \"+ newTransition);\n\n        eventTransitions.put(event, newTransition);\n        return newTransition;\n    }\n\n    @Override\n    public List<Transition<S, E, C>> addTransitions(E event, List<State<S, E, C>> targets, TransitionType transitionType) {\n        List<Transition<S, E, C>> result = new ArrayList<>();\n        for (State<S, E, C> target : targets) {\n            Transition<S, E, C> secTransition = addTransition(event, target, transitionType);\n            result.add(secTransition);\n        }\n\n        return result;\n    }\n\n    @Override\n    public List<Transition<S, E, C>> getEventTransitions(E event) {\n        return eventTransitions.get(event);\n    }\n\n    @Override\n    public Collection<Transition<S, E, C>> getAllTransitions() {\n        return eventTransitions.allTransitions();\n    }\n\n    @Override\n    public S getId() {\n        return stateId;\n    }\n\n    @Override\n    public String accept(Visitor visitor) {\n        String entry = visitor.visitOnEntry(this);\n        String exit = visitor.visitOnExit(this);\n        return entry + exit;\n    }\n\n    @Override\n    public boolean equals(Object anObject){\n        if(anObject instanceof State){\n            State other = (State)anObject;\n            if(this.stateId.equals(other.getId()))\n                return true;\n        }\n        return false;\n    }\n\n    @Override\n    public String toString(){\n        return stateId.toString();\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/StateMachineException.java",
    "content": "package com.alibaba.cola.statemachine.impl;\n\n/**\n * StateMachineException\n *\n * @author Frank Zhang\n * @date 2020-02-08 5:28 PM\n */\npublic class StateMachineException extends RuntimeException{\n    public StateMachineException(String message){\n        super(message);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/StateMachineImpl.java",
    "content": "package com.alibaba.cola.statemachine.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.cola.statemachine.State;\nimport com.alibaba.cola.statemachine.StateMachine;\nimport com.alibaba.cola.statemachine.Transition;\nimport com.alibaba.cola.statemachine.Visitor;\nimport com.alibaba.cola.statemachine.builder.FailCallback;\n\n/**\n * For performance consideration,\n * The state machine is made \"stateless\" on purpose.\n * Once it's built, it can be shared by multi-thread\n * <p>\n * One side effect is since the state machine is stateless, we can not get current state from State Machine.\n *\n * @author Frank Zhang\n * @date 2020-02-07 5:40 PM\n */\npublic class StateMachineImpl<S, E, C> implements StateMachine<S, E, C> {\n\n    private String machineId;\n\n    private final Map<S, State<S, E, C>> stateMap;\n\n    private boolean ready;\n\n    private FailCallback<S, E, C> failCallback;\n\n    public StateMachineImpl(Map<S, State<S, E, C>> stateMap) {\n        this.stateMap = stateMap;\n    }\n\n    @Override\n    public boolean verify(S sourceStateId, E event) {\n        isReady();\n\n        State sourceState = getState(sourceStateId);\n\n        List<Transition<S, E, C>> transitions = sourceState.getEventTransitions(event);\n\n        return transitions != null && transitions.size() != 0;\n    }\n\n    @Override\n    public S fireEvent(S sourceStateId, E event, C ctx) {\n        isReady();\n        Transition<S, E, C> transition = routeTransition(sourceStateId, event, ctx);\n\n        if (transition == null) {\n            Debugger.debug(\"There is no Transition for \" + event);\n            failCallback.onFail(sourceStateId, event, ctx);\n            return sourceStateId;\n        }\n\n        return transition.transit(ctx, false).getId();\n    }\n    @Override\n    public List<S> fireParallelEvent(S sourceState, E event, C context) {\n        isReady();\n        List<Transition<S, E, C>> transitions = routeTransitions(sourceState, event, context);\n        List<S> result = new ArrayList<>();\n        if (transitions == null||transitions.isEmpty()) {\n            Debugger.debug(\"There is no Transition for \" + event);\n            failCallback.onFail(sourceState, event, context);\n            result.add(sourceState);\n            return result;\n        }\n        for (Transition<S, E, C> transition : transitions) {\n            S id = transition.transit(context, false).getId();\n            result.add(id);\n        }\n        return result;\n    }\n\n    private Transition<S, E, C> routeTransition(S sourceStateId, E event, C ctx) {\n        State sourceState = getState(sourceStateId);\n\n        List<Transition<S, E, C>> transitions = sourceState.getEventTransitions(event);\n\n        if (transitions == null || transitions.size() == 0) {\n            return null;\n        }\n\n        Transition<S, E, C> transit = null;\n        for (Transition<S, E, C> transition : transitions) {\n            if (transition.getCondition() == null) {\n                transit = transition;\n            } else if (transition.getCondition().isSatisfied(ctx)) {\n                transit = transition;\n                break;\n            }\n        }\n\n        return transit;\n    }\n    private List<Transition<S,E,C>> routeTransitions(S sourceStateId, E event, C context) {\n        State sourceState = getState(sourceStateId);\n        List<Transition<S, E, C>> result = new ArrayList<>();\n        List<Transition<S, E, C>> transitions = sourceState.getEventTransitions(event);\n        if (transitions == null || transitions.size() == 0) {\n            return null;\n        }\n\n        for (Transition<S, E, C> transition : transitions) {\n            Transition<S, E, C> transit = null;\n            if (transition.getCondition() == null) {\n                transit = transition;\n            } else if (transition.getCondition().isSatisfied(context)) {\n                transit = transition;\n            }\n            result.add(transit);\n        }\n        return result;\n    }\n\n    private State getState(S currentStateId) {\n        State state = StateHelper.getState(stateMap, currentStateId);\n        if (state == null) {\n            showStateMachine();\n            throw new StateMachineException(currentStateId + \" is not found, please check state machine\");\n        }\n        return state;\n    }\n\n    private void isReady() {\n        if (!ready) {\n            throw new StateMachineException(\"State machine is not built yet, can not work\");\n        }\n    }\n\n    @Override\n    public String accept(Visitor visitor) {\n        StringBuilder sb = new StringBuilder();\n        sb.append(visitor.visitOnEntry(this));\n        for (State state : stateMap.values()) {\n            sb.append(state.accept(visitor));\n        }\n        sb.append(visitor.visitOnExit(this));\n        return sb.toString();\n    }\n\n    @Override\n    public void showStateMachine() {\n        SysOutVisitor sysOutVisitor = new SysOutVisitor();\n        accept(sysOutVisitor);\n    }\n\n    @Override\n    public String generatePlantUML() {\n        PlantUMLVisitor plantUMLVisitor = new PlantUMLVisitor();\n        return accept(plantUMLVisitor);\n    }\n\n    @Override\n    public String getMachineId() {\n        return machineId;\n    }\n\n    public void setMachineId(String machineId) {\n        this.machineId = machineId;\n    }\n\n    public void setReady(boolean ready) {\n        this.ready = ready;\n    }\n\n    public void setFailCallback(FailCallback<S, E, C> failCallback) {\n        this.failCallback = failCallback;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/SysOutVisitor.java",
    "content": "package com.alibaba.cola.statemachine.impl;\n\nimport com.alibaba.cola.statemachine.State;\nimport com.alibaba.cola.statemachine.StateMachine;\nimport com.alibaba.cola.statemachine.Transition;\nimport com.alibaba.cola.statemachine.Visitor;\n\n/**\n * SysOutVisitor\n *\n * @author Frank Zhang\n * @date 2020-02-08 8:48 PM\n */\npublic class SysOutVisitor implements Visitor {\n\n    @Override\n    public String visitOnEntry(StateMachine<?, ?, ?> stateMachine) {\n        String entry = \"-----StateMachine:\"+stateMachine.getMachineId()+\"-------\";\n        System.out.println(entry);\n        return entry;\n    }\n\n    @Override\n    public String visitOnExit(StateMachine<?, ?, ?> stateMachine) {\n        String exit = \"------------------------\";\n        System.out.println(exit);\n        return exit;\n    }\n\n    @Override\n    public String visitOnEntry(State<?, ?, ?> state) {\n        StringBuilder sb = new StringBuilder();\n        String stateStr = \"State:\"+state.getId();\n        sb.append(stateStr).append(LF);\n        System.out.println(stateStr);\n        for(Transition transition: state.getAllTransitions()){\n            String transitionStr = \"    Transition:\"+transition;\n            sb.append(transitionStr).append(LF);\n            System.out.println(transitionStr);\n        }\n        return sb.toString();\n    }\n\n    @Override\n    public String visitOnExit(State<?, ?, ?> visitable) {\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/TransitionImpl.java",
    "content": "package com.alibaba.cola.statemachine.impl;\n\nimport com.alibaba.cola.statemachine.Action;\nimport com.alibaba.cola.statemachine.Condition;\nimport com.alibaba.cola.statemachine.State;\nimport com.alibaba.cola.statemachine.Transition;\n\n/**\n * TransitionImpl。\n *\n * This should be designed to be immutable, so that there is no thread-safe risk\n *\n * @author Frank Zhang\n * @date 2020-02-07 10:32 PM\n */\npublic class TransitionImpl<S,E,C> implements Transition<S,E,C> {\n\n    private State<S, E, C> source;\n\n    private State<S, E, C> target;\n\n    private E event;\n\n    private Condition<C> condition;\n\n    private Action<S,E,C> action;\n\n    private TransitionType type = TransitionType.EXTERNAL;\n\n    @Override\n    public State<S, E, C> getSource() {\n        return source;\n    }\n\n    @Override\n    public void setSource(State<S, E, C> state) {\n        this.source = state;\n    }\n\n    @Override\n    public E getEvent() {\n        return this.event;\n    }\n\n    @Override\n    public void setEvent(E event) {\n        this.event = event;\n    }\n\n    @Override\n    public void setType(TransitionType type) {\n        this.type = type;\n    }\n\n    @Override\n    public State<S, E, C> getTarget() {\n        return this.target;\n    }\n\n    @Override\n    public void setTarget(State<S, E, C> target) {\n        this.target = target;\n    }\n\n    @Override\n    public Condition<C> getCondition() {\n        return this.condition;\n    }\n\n    @Override\n    public void setCondition(Condition<C> condition) {\n        this.condition = condition;\n    }\n\n    @Override\n    public Action<S, E, C> getAction() {\n        return this.action;\n    }\n\n    @Override\n    public void setAction(Action<S, E, C> action) {\n        this.action = action;\n    }\n\n    @Override\n    public State<S, E, C> transit(C ctx, boolean checkCondition) {\n        Debugger.debug(\"Do transition: \"+this);\n        this.verify();\n        if (!checkCondition || condition == null || condition.isSatisfied(ctx)) {\n            if(action != null){\n                action.execute(source.getId(), target.getId(), event, ctx);\n            }\n            return target;\n        }\n\n        Debugger.debug(\"Condition is not satisfied, stay at the \"+source+\" state \");\n        return source;\n    }\n\n    @Override\n    public final String toString() {\n        return source + \"-[\" + event.toString() +\", \"+type+\"]->\" + target;\n    }\n\n    @Override\n    public boolean equals(Object anObject){\n        if(anObject instanceof Transition){\n            Transition other = (Transition)anObject;\n            if(this.event.equals(other.getEvent())\n                    && this.source.equals(other.getSource())\n                    && this.target.equals(other.getTarget())){\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public void verify() {\n        if(type== TransitionType.INTERNAL && source != target) {\n            throw new StateMachineException(String.format(\"Internal transition source state '%s' \" +\n                    \"and target state '%s' must be same.\", source, target));\n        }\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/TransitionType.java",
    "content": "package com.alibaba.cola.statemachine.impl;\n\n/**\n * TransitionType\n *\n * @author Frank Zhang\n * @date 2020-02-07 10:23 PM\n */\npublic enum TransitionType {\n    /**\n     * Implies that the Transition, if triggered, occurs without exiting or entering the source State\n     * (i.e., it does not cause a state change). This means that the entry or exit condition of the source\n     * State will not be invoked. An internal Transition can be taken even if the SateMachine is in one or\n     * more Regions nested within the associated State.\n     */\n    INTERNAL,\n    /**\n     * Implies that the Transition, if triggered, will not exit the composite (source) State, but it\n     * will exit and re-enter any state within the composite State that is in the current state configuration.\n     */\n    LOCAL,\n    /**\n     * Implies that the Transition, if triggered, will exit the composite (source) State.\n     */\n    EXTERNAL\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/test/java/com/alibaba/cola/test/StateMachineChoiceTest.java",
    "content": "package com.alibaba.cola.test;\n\nimport com.alibaba.cola.statemachine.Action;\nimport com.alibaba.cola.statemachine.Condition;\nimport com.alibaba.cola.statemachine.StateMachine;\nimport com.alibaba.cola.statemachine.builder.StateMachineBuilder;\nimport com.alibaba.cola.statemachine.builder.StateMachineBuilderFactory;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n\n/**\n * @author dingchenchen\n * @since 2021/1/6\n */\npublic class StateMachineChoiceTest {\n\n    static class Context{\n        private String condition;\n\n        public Context(String condition){\n            this.condition = condition;\n        }\n\n        public String getCondition() {\n            return condition;\n        }\n    }\n\n    /**\n     * 测试选择分支，针对同一个事件：EVENT1\n     * if condition == \"1\", STATE1 --> STATE1\n     * if condition == \"2\" , STATE1 --> STATE2\n     * if condition == \"3\" , STATE1 --> STATE3\n     */\n    @Test\n    public void testChoice(){\n        StateMachineBuilder<StateMachineTest.States, StateMachineTest.Events, Context> builder = StateMachineBuilderFactory.create();\n        builder.internalTransition()\n            .within(StateMachineTest.States.STATE1)\n            .on(StateMachineTest.Events.EVENT1)\n            .when(checkCondition1())\n            .perform(doAction());\n        builder.externalTransition()\n            .from(StateMachineTest.States.STATE1)\n            .to(StateMachineTest.States.STATE2)\n            .on(StateMachineTest.Events.EVENT1)\n            .when(checkCondition2())\n            .perform(doAction());\n        builder.externalTransition()\n            .from(StateMachineTest.States.STATE1)\n            .to(StateMachineTest.States.STATE3)\n            .on(StateMachineTest.Events.EVENT1)\n            .when(checkCondition3())\n            .perform(doAction());\n\n        StateMachine<StateMachineTest.States, StateMachineTest.Events, Context> stateMachine = builder.build(\"ChoiceConditionMachine\");\n        StateMachineTest.States target1 = stateMachine.fireEvent(StateMachineTest.States.STATE1, StateMachineTest.Events.EVENT1, new Context(\"1\"));\n        Assertions.assertEquals(StateMachineTest.States.STATE1,target1);\n        StateMachineTest.States target2 = stateMachine.fireEvent(StateMachineTest.States.STATE1, StateMachineTest.Events.EVENT1, new Context(\"2\"));\n        Assertions.assertEquals(StateMachineTest.States.STATE2,target2);\n        StateMachineTest.States target3 = stateMachine.fireEvent(StateMachineTest.States.STATE1, StateMachineTest.Events.EVENT1, new Context(\"3\"));\n        Assertions.assertEquals(StateMachineTest.States.STATE3,target3);\n    }\n\n    private Condition<Context> checkCondition1() {\n        return  (ctx) -> \"1\".equals(ctx.getCondition());\n    }\n\n    private Condition<Context> checkCondition2() {\n        return (ctx) -> \"2\".equals(ctx.getCondition());\n    }\n\n    private Condition<Context> checkCondition3() {\n        return (ctx) -> \"3\".equals(ctx.getCondition());\n    }\n\n    private Action<StateMachineTest.States, StateMachineTest.Events, Context> doAction() {\n        return (from, to, event, ctx)->{\n            System.out.println(\"from:\"+from+\" to:\"+to+\" on:\"+event+\" condition:\" + ctx.getCondition());\n        };\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/test/java/com/alibaba/cola/test/StateMachinePlantUMLTest.java",
    "content": "package com.alibaba.cola.test;\n\nimport com.alibaba.cola.statemachine.Action;\nimport com.alibaba.cola.statemachine.Condition;\nimport com.alibaba.cola.statemachine.StateMachine;\nimport com.alibaba.cola.statemachine.builder.StateMachineBuilder;\nimport com.alibaba.cola.statemachine.builder.StateMachineBuilderFactory;\nimport com.alibaba.cola.statemachine.impl.Debugger;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.stream.Stream;\n\nimport static com.alibaba.cola.test.StateMachinePlantUMLTest.PriceAdjustmentTaskEventEnum.*;\nimport static com.alibaba.cola.test.StateMachinePlantUMLTest.PriceAdjustmentTaskStatusEnum.*;\n\n/**\n * StateMachinePlantUMLTest\n *\n * @author Frank Zhang\n * @date 2020-02-09 7:53 PM\n */\npublic class StateMachinePlantUMLTest {\n\n    static enum PriceAdjustmentTaskStatusEnum {\n        /**\n         * 开始状态\n         */\n        None,\n        /**\n         * 待商家处理\n         */\n        Supplier_Processing,\n        /**\n         * 待控商小二处理\n         */\n        Supplier_Manager_Processing,\n        /**\n         * 待价格管控小二处理\n         */\n        Price_Manager_Processing,\n        /**\n         * 退出\n         */\n        Closed\n    }\n\n    static enum PriceAdjustmentTaskEventEnum {\n\n        // 系统事件\n        Create,\n        Normal_Update,\n        /**\n         * 合理价变更\n         */\n        P0_Changed,\n        /**\n         * 页面价变合理\n         */\n        Page_Price_changed,\n\n        // 商家事件\n        Supplier_Reject,\n        Supplier_Agree,\n        Supplier_Timeout,\n\n        // 控商小二事件\n        Apply_Over_P0_Sell,\n\n        // 价格小二事件\n        Agree_Over_P0_Sell,\n        Reject_Over_P0_Sell;\n\n        public boolean isSupplierTimeout() {\n            return this == Supplier_Timeout;\n        }\n\n        public boolean isSystemEvent(){\n            return  this == Create ||\n                    this == Normal_Update ||\n                    this == P0_Changed ||\n                    this == Page_Price_changed;\n        }\n    }\n\n    @BeforeEach\n    public void init(){\n        Debugger.enableDebug();\n    }\n\n    @Test\n    public void testPlantUML(){\n        StateMachineBuilder<PriceAdjustmentTaskStatusEnum, PriceAdjustmentTaskEventEnum, StateMachineTest.Context> builder = StateMachineBuilderFactory.create();\n\n        builder.externalTransition()\n                .from(None)\n                .to(Supplier_Processing)\n                .on(Create)\n                .when(checkCondition())\n                .perform(doAction());\n\n        // 商家调价\n        Stream.of(Supplier_Processing, Supplier_Manager_Processing, Price_Manager_Processing)\n                .forEach(status ->\n                        builder.externalTransition()\n                                .from(status)\n                                .to(Closed)\n                                .on(Supplier_Agree)\n                                .when(checkCondition())\n                                .perform(doAction())\n                );\n\n        // 商家 -上升至-> 控商小二\n        builder.externalTransition()\n                .from(Supplier_Processing)\n                .to(Supplier_Manager_Processing)\n                .on(Supplier_Reject)\n                .when(checkCondition())\n                .perform(doAction());\n\n        builder.externalTransition()\n                .from(Supplier_Processing)\n                .to(Supplier_Manager_Processing)\n                .on(Supplier_Timeout)\n                .when(checkCondition())\n                .perform(doAction());\n\n        // 申请申请高于P0售卖\n        builder.externalTransition()\n                .from(Supplier_Manager_Processing)\n                .to(Price_Manager_Processing)\n                .on(Apply_Over_P0_Sell)\n                .when(checkCondition())\n                .perform(doAction());\n\n        // 同意高于P0价售卖\n        builder.externalTransition()\n                .from(Price_Manager_Processing)\n                .to(Closed)\n                .on(Agree_Over_P0_Sell)\n                .when(checkCondition())\n                .perform(doAction());\n\n        // 拒绝高于P0价售卖\n        builder.externalTransition()\n                .from(Price_Manager_Processing)\n                .to(Supplier_Manager_Processing)\n                .on(Reject_Over_P0_Sell)\n                .when(checkCondition())\n                .perform(doAction());\n\n        // 普通字段更新事件\n        Stream.of(Supplier_Processing, Supplier_Manager_Processing, Price_Manager_Processing)\n                .forEach(status -> builder\n                        .internalTransition()\n                        .within(status)\n                        .on(Normal_Update)\n                        .when(checkCondition())\n                        .perform(doAction())\n                );\n\n        // P0价变更事件、页面价高于合理价事件\n        Stream.of(P0_Changed, Page_Price_changed)\n                .forEach(event -> builder.externalTransitions()\n                        .fromAmong(Supplier_Processing, Supplier_Manager_Processing, Price_Manager_Processing)\n                        .to(Closed)\n                        .on(event)\n                        .when(checkCondition())\n                        .perform(doAction()));\n\n        StateMachine stateMachine = builder.build(\"AdjustPriceTask\");\n        String plantUML = stateMachine.generatePlantUML();\n        System.out.println(plantUML);\n\n    }\n\n    private Condition<StateMachineTest.Context> checkCondition() {\n        return (ctx) -> {return true;};\n    }\n\n    private Action<PriceAdjustmentTaskStatusEnum, PriceAdjustmentTaskEventEnum, StateMachineTest.Context> doAction() {\n        return (from, to, event, ctx)->{\n            System.out.println(ctx.operator+\" is operating \"+ctx.entityId+\" from:\"+from+\" to:\"+to+\" on:\"+event);\n        };\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/test/java/com/alibaba/cola/test/StateMachineTest.java",
    "content": "package com.alibaba.cola.test;\n\nimport com.alibaba.cola.statemachine.Action;\nimport com.alibaba.cola.statemachine.Condition;\nimport com.alibaba.cola.statemachine.StateMachine;\nimport com.alibaba.cola.statemachine.StateMachineFactory;\nimport com.alibaba.cola.statemachine.builder.AlertFailCallback;\nimport com.alibaba.cola.statemachine.builder.StateMachineBuilder;\nimport com.alibaba.cola.statemachine.builder.StateMachineBuilderFactory;\nimport com.alibaba.cola.statemachine.exception.TransitionFailException;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n\nimport java.util.List;\n\n/**\n * StateMachineTest\n *\n * @author Frank Zhang\n * @date 2020-02-08 12:19 PM\n */\npublic class StateMachineTest {\n\n    static String MACHINE_ID = \"TestStateMachine\";\n\n    static enum States {\n        STATE1,\n        STATE2,\n        STATE3,\n        STATE4\n    }\n\n    static enum Events {\n        EVENT1,\n        EVENT2,\n        EVENT3,\n        EVENT4,\n        INTERNAL_EVENT\n    }\n\n    static class Context {\n        String operator = \"frank\";\n        String entityId = \"123465\";\n    }\n\n    @Test\n    public void testExternalNormal() {\n        StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();\n        builder.externalTransition()\n            .from(States.STATE1)\n            .to(States.STATE2)\n            .on(Events.EVENT1)\n            .when(checkCondition())\n            .perform(doAction());\n\n        StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID);\n        States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context());\n        Assertions.assertEquals(States.STATE2, target);\n    }\n\n    @Test\n    public void testFail() {\n        StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();\n        builder.externalTransition()\n            .from(States.STATE1)\n            .to(States.STATE2)\n            .on(Events.EVENT1)\n            .when(checkCondition())\n            .perform(doAction());\n\n        builder.setFailCallback(new AlertFailCallback<>());\n\n        StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID + \"-testFail\");\n        Assertions.assertThrows(TransitionFailException.class,\n            () -> stateMachine.fireEvent(States.STATE2, Events.EVENT1, new Context()));\n    }\n\n    @Test\n    public void testVerify() {\n        StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();\n        builder.externalTransition()\n            .from(States.STATE1)\n            .to(States.STATE2)\n            .on(Events.EVENT1)\n            .when(checkCondition())\n            .perform(doAction());\n\n        StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID + \"-testVerify\");\n\n        Assertions.assertTrue(stateMachine.verify(States.STATE1, Events.EVENT1));\n        Assertions.assertFalse(stateMachine.verify(States.STATE1, Events.EVENT2));\n    }\n\n    @Test\n    public void testExternalTransitionsNormal() {\n        StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();\n        builder.externalTransitions()\n            .fromAmong(States.STATE1, States.STATE2, States.STATE3)\n            .to(States.STATE4)\n            .on(Events.EVENT1)\n            .when(checkCondition())\n            .perform(doAction());\n\n        StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID + \"1\");\n        States target = stateMachine.fireEvent(States.STATE2, Events.EVENT1, new Context());\n        Assertions.assertEquals(States.STATE4, target);\n    }\n\n    @Test\n    public void testInternalNormal() {\n        StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();\n        builder.internalTransition()\n            .within(States.STATE1)\n            .on(Events.INTERNAL_EVENT)\n            .when(checkCondition())\n            .perform(doAction());\n        StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID + \"2\");\n\n        stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context());\n        States target = stateMachine.fireEvent(States.STATE1, Events.INTERNAL_EVENT, new Context());\n        Assertions.assertEquals(States.STATE1, target);\n    }\n\n    @Test\n    public void testExternalInternalNormal() {\n        StateMachine<States, Events, Context> stateMachine = buildStateMachine(\"testExternalInternalNormal\");\n\n        Context context = new Context();\n        States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, context);\n        Assertions.assertEquals(States.STATE2, target);\n        target = stateMachine.fireEvent(States.STATE2, Events.INTERNAL_EVENT, context);\n        Assertions.assertEquals(States.STATE2, target);\n        target = stateMachine.fireEvent(States.STATE2, Events.EVENT2, context);\n        Assertions.assertEquals(States.STATE1, target);\n        target = stateMachine.fireEvent(States.STATE1, Events.EVENT3, context);\n        Assertions.assertEquals(States.STATE3, target);\n    }\n\n    private StateMachine<States, Events, Context> buildStateMachine(String machineId) {\n        StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();\n        builder.externalTransition()\n            .from(States.STATE1)\n            .to(States.STATE2)\n            .on(Events.EVENT1)\n            .when(checkCondition())\n            .perform(doAction());\n\n        builder.internalTransition()\n            .within(States.STATE2)\n            .on(Events.INTERNAL_EVENT)\n            .when(checkCondition())\n            .perform(doAction());\n\n        builder.externalTransition()\n            .from(States.STATE2)\n            .to(States.STATE1)\n            .on(Events.EVENT2)\n            .when(checkCondition())\n            .perform(doAction());\n\n        builder.externalTransition()\n            .from(States.STATE1)\n            .to(States.STATE3)\n            .on(Events.EVENT3)\n            .when(checkCondition())\n            .perform(doAction());\n\n        builder.externalTransitions()\n            .fromAmong(States.STATE1, States.STATE2, States.STATE3)\n            .to(States.STATE4)\n            .on(Events.EVENT4)\n            .when(checkCondition())\n            .perform(doAction());\n\n        builder.build(machineId);\n\n        StateMachine<States, Events, Context> stateMachine = StateMachineFactory.get(machineId);\n        stateMachine.showStateMachine();\n        return stateMachine;\n    }\n\n    @Test\n    public void testMultiThread() {\n        buildStateMachine(\"testMultiThread\");\n\n        for (int i = 0; i < 10; i++) {\n            Thread thread = new Thread(() -> {\n                StateMachine<States, Events, Context> stateMachine = StateMachineFactory.get(\"testMultiThread\");\n                States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context());\n                Assertions.assertEquals(States.STATE2, target);\n            });\n            thread.start();\n        }\n\n        for (int i = 0; i < 10; i++) {\n            Thread thread = new Thread(() -> {\n                StateMachine<States, Events, Context> stateMachine = StateMachineFactory.get(\"testMultiThread\");\n                States target = stateMachine.fireEvent(States.STATE1, Events.EVENT4, new Context());\n                Assertions.assertEquals(States.STATE4, target);\n            });\n            thread.start();\n        }\n\n        for (int i = 0; i < 10; i++) {\n            Thread thread = new Thread(() -> {\n                StateMachine<States, Events, Context> stateMachine = StateMachineFactory.get(\"testMultiThread\");\n                States target = stateMachine.fireEvent(States.STATE1, Events.EVENT3, new Context());\n                Assertions.assertEquals(States.STATE3, target);\n            });\n            thread.start();\n        }\n\n    }\n    @Test\n    public void testParallel(){\n        StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();\n        builder.externalParallelTransition()\n                .from(States.STATE1)\n                .toAmong(States.STATE2,States.STATE3)\n                .on(StateMachineTest.Events.EVENT1)\n                .when(checkCondition())\n                .perform(doAction());\n        builder.externalTransitions()\n                .fromAmong(StateMachineTest.States.STATE2,StateMachineTest.States.STATE3)\n                .to(StateMachineTest.States.STATE4)\n                .on(StateMachineTest.Events.EVENT2)\n                .when(checkCondition())\n                .perform(doAction());\n        StateMachine<States, Events, Context> stateMachine = builder.build(\"ParallelMachine\");\n        System.out.println(stateMachine.generatePlantUML());\n        List<States> states = stateMachine.fireParallelEvent(StateMachineTest.States.STATE1, StateMachineTest.Events.EVENT1, new Context());\n        for (StateMachineTest.States state : states) {\n            System.out.println(state);\n        }\n        States target2 = stateMachine.fireEvent(StateMachineTest.States.STATE2, StateMachineTest.Events.EVENT2, new Context());\n        Assertions.assertEquals(States.STATE4,target2);\n        States target3 = stateMachine.fireEvent(StateMachineTest.States.STATE3, StateMachineTest.Events.EVENT2, new Context());\n        Assertions.assertEquals(States.STATE4,target3);\n    }\n\n    private Condition<Context> checkCondition() {\n        return new Condition<Context>() {\n            @Override\n            public boolean isSatisfied(Context context) {\n                System.out.println(\"Check condition : \" + context);\n                return true;\n            }\n        };\n    }\n\n    private Action<States, Events, Context> doAction() {\n        return (from, to, event, ctx) -> {\n            System.out.println(\n                ctx.operator + \" is operating \" + ctx.entityId + \" from:\" + from + \" to:\" + to + \" on:\" + event);\n        };\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-statemachine/src/test/java/com/alibaba/cola/test/StateMachineUnNormalTest.java",
    "content": "package com.alibaba.cola.test;\n\nimport com.alibaba.cola.statemachine.Action;\nimport com.alibaba.cola.statemachine.Condition;\nimport com.alibaba.cola.statemachine.StateMachine;\nimport com.alibaba.cola.statemachine.builder.StateMachineBuilder;\nimport com.alibaba.cola.statemachine.builder.StateMachineBuilderFactory;\nimport com.alibaba.cola.statemachine.impl.StateMachineException;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * StateMachineUnNormalTest\n *\n * @author Frank Zhang\n * @date 2020-02-08 5:52 PM\n */\npublic class StateMachineUnNormalTest {\n\n    @Test\n    public void testConditionNotMeet(){\n        StateMachineBuilder<StateMachineTest.States, StateMachineTest.Events, StateMachineTest.Context> builder = StateMachineBuilderFactory.create();\n        builder.externalTransition()\n                .from(StateMachineTest.States.STATE1)\n                .to(StateMachineTest.States.STATE2)\n                .on(StateMachineTest.Events.EVENT1)\n                .when(checkConditionFalse())\n                .perform(doAction());\n\n        StateMachine<StateMachineTest.States, StateMachineTest.Events, StateMachineTest.Context> stateMachine = builder.build(\"NotMeetConditionMachine\");\n        StateMachineTest.States target = stateMachine.fireEvent(StateMachineTest.States.STATE1, StateMachineTest.Events.EVENT1, new StateMachineTest.Context());\n        Assertions.assertEquals(StateMachineTest.States.STATE1,target);\n    }\n\n\n    @Test\n    public void testDuplicatedTransition(){\n        Assertions.assertThrows(StateMachineException.class, ()->{\n            StateMachineBuilder<StateMachineTest.States, StateMachineTest.Events, StateMachineTest.Context> builder = StateMachineBuilderFactory.create();\n            builder.externalTransition()\n                    .from(StateMachineTest.States.STATE1)\n                    .to(StateMachineTest.States.STATE2)\n                    .on(StateMachineTest.Events.EVENT1)\n                    .when(checkCondition())\n                    .perform(doAction());\n\n            builder.externalTransition()\n                    .from(StateMachineTest.States.STATE1)\n                    .to(StateMachineTest.States.STATE2)\n                    .on(StateMachineTest.Events.EVENT1)\n                    .when(checkCondition())\n                    .perform(doAction());\n        });\n    }\n\n    @Test\n    public void testDuplicateMachine(){\n        Assertions.assertThrows(StateMachineException.class, ()-> {\n            StateMachineBuilder<StateMachineTest.States, StateMachineTest.Events, StateMachineTest.Context> builder = StateMachineBuilderFactory.create();\n            builder.externalTransition()\n                    .from(StateMachineTest.States.STATE1)\n                    .to(StateMachineTest.States.STATE2)\n                    .on(StateMachineTest.Events.EVENT1)\n                    .when(checkCondition())\n                    .perform(doAction());\n\n            builder.build(\"DuplicatedMachine\");\n            builder.build(\"DuplicatedMachine\");\n        });\n    }\n\n    private Condition<StateMachineTest.Context> checkCondition() {\n        return (ctx) -> {return true;};\n    }\n\n    private Condition<StateMachineTest.Context> checkConditionFalse() {\n        return (ctx) -> {return false;};\n    }\n\n    private Action<StateMachineTest.States, StateMachineTest.Events, StateMachineTest.Context> doAction() {\n        return (from, to, event, ctx)->{\n            System.out.println(ctx.operator+\" is operating \"+ctx.entityId+\"from:\"+from+\" to:\"+to+\" on:\"+event);\n        };\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-test-container/README.md",
    "content": "## 作用\n测试工具，当容器启动比较耗时的时候，这个工具特别有用，用法摘要：\n\n启动TestsContainer\n1. 运行测试类，在命令行中输入类全称：com.alibaba.cola.test.Demo\n2. 运行单个方法，在命令行中输入方法引用：com.alibaba.cola.test.Demo#testTwo\n3. 重复运行，输入r\n\n\n\n\n"
  },
  {
    "path": "cola-components/cola-component-test-container/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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    <parent>\n        <groupId>com.alibaba.cola</groupId>\n        <artifactId>cola-components-parent</artifactId>\n        <version>5.x-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>cola-component-test-container</artifactId>\n    <packaging>jar</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <!-- Spring Boot Test -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <!-- Exclude JUnit 3 and JUnit 4 support -->\n            <exclusions>\n                <exclusion>\n                    <groupId>junit</groupId>\n                    <artifactId>junit</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <dependency>\n            <groupId>commons-cli</groupId>\n            <artifactId>commons-cli</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.junit.platform</groupId>\n            <artifactId>junit-platform-launcher</artifactId>\n            <version>1.9.3</version>\n        </dependency>\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter-engine</artifactId>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/main/java/com/alibaba/cola/test/BeanMetaUtils.java",
    "content": "package com.alibaba.cola.test;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\n\n/**\n * BeanMetaUtils\n *\n * @author Frank Zhang\n * @date 2020-11-17 4:49 PM\n */\npublic class BeanMetaUtils {\n    public static Method findMethod(Class clazz, Class<? extends Annotation> annotationType){\n        Method[] allMethods = clazz.getMethods();\n        for (Method method : allMethods){\n            Annotation[] annotations = method.getAnnotations();\n            for(Annotation item : annotations){\n                if(item.annotationType().equals(annotationType)){\n                    return method;\n                }\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/main/java/com/alibaba/cola/test/TestExecutor.java",
    "content": "package com.alibaba.cola.test;\n\n\nimport com.alibaba.cola.test.command.TestClassRunCmd;\nimport com.alibaba.cola.test.command.TestMethodRunCmd;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\n\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\n\n/**\n * TestExecutor\n *\n * @author Frank Zhang\n * @date 2020-11-17 3:42 PM\n */\npublic class TestExecutor {\n\n\n    private Launcher launcher;\n\n    public TestExecutor(Launcher launcher) {\n        this.launcher = launcher;\n    }\n\n    public void execute(TestClassRunCmd cmd) throws Exception {\n        Class<?> testClz = Class.forName(cmd.getClassName());\n        runClassTest(cmd, testClz);\n    }\n\n    public void execute(TestMethodRunCmd cmd) throws Exception {\n        Class<?> testClz = Class.forName(cmd.getClassName());\n        runMethodTest(cmd, testClz, cmd.getMethodName());\n    }\n\n    private void runMethodTest(TestMethodRunCmd cmd, Class<?> testClz, String methodName) throws Exception {\n        // 获取测试类的方法参数类型name，只支持单参数\n        String paramTypeName = extractParamTypeName(testClz, methodName);\n\n        // 创建测试方法\n        LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder\n                .request()\n                .selectors(selectMethod(testClz, methodName, paramTypeName))\n                .build();\n\n        // 运行测试方法\n        launcher.execute(request, new MyTestExecutionListener());\n    }\n\n    private String extractParamTypeName(Class<?> testClz, String methodName) {\n        for (Method method : testClz.getMethods()) {\n            if(methodName.equals(method.getName())){\n                for (Parameter parameter : method.getParameters()) {\n                    return parameter.getType().getName();\n                }\n            }\n        }\n        return \"\";\n    }\n\n\n    private void runClassTest(TestClassRunCmd cmd, Class<?> testClz) {\n        // 创建测试类\n        LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder\n                .request()\n                .selectors(selectClass(testClz))\n                .build();\n\n        // 运行测试方法\n        launcher.execute(request, new MyTestExecutionListener());\n    }\n\n    static class MyTestExecutionListener implements TestExecutionListener {\n        @Override\n        public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n            if (testExecutionResult.getStatus() == TestExecutionResult.Status.FAILED) {\n                // 处理测试失败的情况，例如记录日志或发送通知\n                System.err.println(\"Test failed: \" + testIdentifier.getDisplayName());\n                testExecutionResult.getThrowable().get().printStackTrace();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/main/java/com/alibaba/cola/test/TestsContainer.java",
    "content": "package com.alibaba.cola.test;\n\nimport com.alibaba.cola.test.command.AbstractCommand;\nimport com.alibaba.cola.test.command.GuideCmd;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.core.LauncherFactory;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.util.ObjectUtils;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * TestsContainer\n *\n * 这是一个轻量级的TDD测试工具，可以敏捷的在开发过程中，运行测试，功能如下：\n * 1.测试单个方法，请在控制台输入方法全称\n *   例如：com.alibaba.framework.sales.service.test.CustomerServiceTest.testCheckConflict()\n * 2.测试整个测试类，请在控制台输入类全称\n *   例如：com.alibaba.framework.sales.service.test.CustomerServiceTest\n * 3.重复上一次测试，只需在控制台输入字母 - ‘r’\n *\n * @author Frank Zhang\n * @date 2020-11-17 3:35 PM\n */\npublic class TestsContainer {\n\n    private static ApplicationContext context;\n    private static Launcher launcher;\n\n    private static TestExecutor testExecutor;\n    private static AtomicBoolean initFlag = new AtomicBoolean(false);\n\n\n    /**\n     * 如果要用到Junit5的Extension功能，需要显示的提供Launcher\n     *\n     * @param context  ApplicationContext to be provided\n     * @param launcher 运行Junit5测试用例的Launcher, 如果不提供，默认会自己创建一个\n     */\n    public static void start(ApplicationContext context, Launcher launcher) {\n        TestsContainer.context = context;\n        if (launcher != null) {\n            TestsContainer.launcher = launcher;\n        } else {\n            TestsContainer.launcher = LauncherFactory.create();\n        }\n        testExecutor = new TestExecutor(TestsContainer.launcher);\n        monitorConsole();\n    }\n\n    /**\n     * 使用Junit5的launcher之后，不再需要ApplicationContext，框架会自己处理Spring的依赖关系\n     * @param launcher\n     */\n    public static void start(Launcher launcher) {\n        start(null, launcher);\n    }\n\n    /**\n     * TestsContainer is optional to be in Spring Container\n     *\n     * @param context ApplicationContext to be provided\n     */\n    public static void start(ApplicationContext context) {\n        start(context, null);\n    }\n\n    /**\n     * TestsContainer without Spring Container\n     */\n    public static void start() {\n        start(null, null);\n    }\n\n    public static void execute(String input) {\n        if (ObjectUtils.isEmpty(input)) {\n            return;\n        }\n        input = input.trim();\n        AbstractCommand command = AbstractCommand.createCmd(input);\n        if (command == null) {\n            System.err.println(\"Your input is not a valid qualified name\");\n            return;\n        }\n\n        command.execute();\n    }\n\n    public static TestExecutor getTestExecutor() {\n        return testExecutor;\n    }\n\n    private static void monitorConsole() {\n        BufferedReader bufferRead = new BufferedReader(new InputStreamReader(\n                System.in));\n        String input = GuideCmd.GUIDE_HELP;\n        while (true) {\n            try {\n                execute(input);\n            } catch (Exception e) {\n                e.printStackTrace();\n            } catch (Error e) {\n                e.printStackTrace();\n                break;\n            }\n            try {\n                input = bufferRead.readLine();\n            } catch (IOException e) {\n                e.printStackTrace();\n                return;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/main/java/com/alibaba/cola/test/command/AbstractCommand.java",
    "content": "package com.alibaba.cola.test.command;\n\nimport org.apache.commons.cli.*;\nimport org.springframework.util.ObjectUtils;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * AbstractCommand\n *\n * @author Frank Zhang\n * @date 2020-11-17 4:33 PM\n */\npublic abstract class AbstractCommand {\n    private static final CommandLineParser parser = new DefaultParser();\n    protected static AbstractCommand curCmd;\n    protected static AbstractCommand preCmd;\n\n    protected String cmdRaw;\n    private Map<String, Object> params;\n    private Options options;\n    private CommandLine commandLine;\n\n    private final String SPACE = \" \";\n    private final String EMPTY = \"\";\n\n    public AbstractCommand(String cmdRaw){\n        this.cmdRaw = cmdRaw.replaceAll(\" +\", SPACE);\n        params = new HashMap<>();\n        options = new Options();\n        initParser(options);\n        commandLine = parse();\n    }\n\n    public void execute(){\n        System.out.println(\"===Run start==== \"+cmdRaw);\n        action();\n        System.out.println(\"===Run end====\\n\");\n    }\n\n    /**\n     * 清理当前命令的上下文\n     */\n    protected void cleanContext(){}\n\n    protected void initParser(Options options){};\n\n    protected abstract void action();\n\n    public CommandLine parse(){\n        try {\n            return parser.parse(options, cmdRaw.split(SPACE));\n        } catch (ParseException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    public boolean isEclipseMethod(String input) {\n        return input.indexOf(\"(\") > 0 ;\n    }\n\n    public boolean isIdeaMethod(String input) {\n        return input.indexOf(\"#\") > 0 ;\n    }\n\n    public CommandLine getCommandLine() {\n        return commandLine;\n    }\n\n    public static AbstractCommand createCmd(String cmdRaw){\n        if(ObjectUtils.isEmpty(cmdRaw)){\n            return null;\n        }\n\n        AbstractCommand command = null;\n\n         if(cmdRaw.matches(CommandEnum.TestMethodRunCmd.getDesc())){\n            command = new TestMethodRunCmd(cmdRaw);\n        } else if(cmdRaw.matches(CommandEnum.TestClassRunCmd.getDesc())){\n            command = new TestClassRunCmd(cmdRaw);\n        }else if(cmdRaw.matches(CommandEnum.GuideCmd.getDesc())){\n            command = new GuideCmd(cmdRaw);\n        }\n\n        if(command != null){\n            preCmd = curCmd;\n            curCmd = command;\n        }\n        if(preCmd != null){\n            preCmd.cleanContext();\n        }\n\n        return command;\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/main/java/com/alibaba/cola/test/command/CommandEnum.java",
    "content": "package com.alibaba.cola.test.command;\n\n/**\n * CommandEnum\n *\n * @author Frank Zhang\n * @date 2020-11-17 4:39 PM\n */\npublic enum CommandEnum {\n    TestMethodRunCmd(\"\", \"[^\\\\s]*[\\\\(#].*\"),\n    TestClassRunCmd(\"\", \"(\\\\w+\\\\.\\\\w+){1,}\"),\n    GuideCmd(\"\", \"^[rhq]$\"),\n    ;\n\n    private String cmd;\n    private String desc;\n\n    CommandEnum(String cmd, String desc){\n        this.cmd = cmd;\n        this.desc = desc;\n    }\n\n    public String getCmd() {\n        return cmd;\n    }\n\n    public void setCmd(String cmd) {\n        this.cmd = cmd;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n\n    public void setDesc(String desc) {\n        this.desc = desc;\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/main/java/com/alibaba/cola/test/command/GuideCmd.java",
    "content": "package com.alibaba.cola.test.command;\n\nimport com.alibaba.cola.test.TestsContainer;\n\n/**\n * GuideCmd\n *\n * @author Frank Zhang\n * @date 2020-11-17 4:41 PM\n */\npublic class GuideCmd extends AbstractCommand {\n\n    public static final String GUIDE_HELP = \"h\";\n    public static final String GUIDE_REPEAT = \"r\";\n    public static final String GUIDE_QUIT = \"q\";\n\n    public GuideCmd(String cmdRaw) {\n        super(cmdRaw);\n    }\n\n    @Override\n    public void execute(){\n        action();\n    }\n\n    @Override\n    protected void action() {\n        if(cmdRaw.equals(GUIDE_HELP)){\n            System.out.println(\"************** 欢迎使用轻量级TDD测试工具 ***************************\");\n            System.out.println(\"**** 1.测试单个方法，请在控制台输入方法全称\");\n            System.out.println(\"**** 例如：com.alibaba.framework.sales.service.test.CustomerServiceTest.testCheckConflict()\");\n            System.out.println(\"**** 2.测试整个测试类，请在控制台输入类全称\");\n            System.out.println(\"**** 例如：com.alibaba.framework.sales.service.test.CustomerServiceTest\");\n            System.out.println(\"**** 3.重复上一次测试，只需在控制台输入字母 - ‘r’\");\n            System.out.println(\"**** 4.自动生成ColaTest测试类,请输入‘new 方法全称  参数1 参数2 ...’\");\n            System.out.println(\"**** 例如：new com.alibaba.crm.sales.domain.customer.entity.CustomerE#addContact\");\n            System.out.println(\"***********************************************************************************\");\n        }else if(cmdRaw.equals(GUIDE_REPEAT)){\n            TestsContainer.execute(preCmd.cmdRaw);\n        }else if(cmdRaw.equals(GUIDE_QUIT)){\n            System.exit(0);\n            throw new Error(\"强制退出\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/main/java/com/alibaba/cola/test/command/TestClassRunCmd.java",
    "content": "package com.alibaba.cola.test.command;\n\nimport com.alibaba.cola.test.TestsContainer;\n\n/**\n * TestClassRunCmd\n *\n * @author Frank Zhang\n * @date 2020-11-17 4:42 PM\n */\npublic class TestClassRunCmd extends AbstractCommand {\n    private String className;\n\n    public TestClassRunCmd(String cmdRaw) {\n        super(cmdRaw);\n        this.className = cmdRaw;\n    }\n\n    @Override\n    protected void action() {\n        try {\n            TestsContainer.getTestExecutor().execute(this);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    public String getClassName() {\n        return className;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/main/java/com/alibaba/cola/test/command/TestMethodRunCmd.java",
    "content": "package com.alibaba.cola.test.command;\n\nimport com.alibaba.cola.test.TestsContainer;\nimport org.apache.commons.cli.Option;\nimport org.apache.commons.cli.Options;\nimport org.springframework.core.type.filter.RegexPatternTypeFilter;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * TestMethodRunCmd\n *\n * @author Frank Zhang\n * @date 2020-11-17 4:43 PM\n */\npublic class TestMethodRunCmd extends AbstractCommand {\n\n    private static final String RE_RECORD = \"rr\";\n    public final static String DOT = \".\";\n    public final static String NOTE_SYMBOL = \"#\";\n    private String methodName;\n    private String className;\n    /** 是否片段录制*/\n    private boolean segmentRecord = false;\n    List<RegexPatternTypeFilter> recordFilters = new ArrayList<>();\n\n    public TestMethodRunCmd(String cmdRaw) {\n        super(cmdRaw);\n        parseCommand();\n    }\n\n    @Override\n    protected void action() {\n        try {\n            TestsContainer.getTestExecutor().execute(this);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    protected void initParser(Options options) {\n        Option point = Option.builder(RE_RECORD)\n                .hasArgs()\n                .argName(\"p1,p2...\")\n                .valueSeparator(',')\n                .desc(\"A directories list with ',' separate to handle its child files\")\n                .build();\n        options.addOption(point);\n    }\n\n    public String getMethodName() {\n        return methodName;\n    }\n\n    public String getClassName() {\n        return className;\n    }\n\n    public boolean isSegmentRecord() {\n        return segmentRecord;\n    }\n\n    private void parseCommand(){\n        String cmd = getCommandLine().getArgs()[0];\n        if (isEclipseMethod(cmd)) {\n            methodName = cmd.substring(cmd.lastIndexOf(DOT)+1, cmd.indexOf(\"(\"));\n            className = cmd.substring(0, cmd.lastIndexOf(DOT));\n        }\n        if (isIdeaMethod(cmd)) {\n            methodName = cmd.substring(cmd.lastIndexOf(NOTE_SYMBOL)+1, cmd.length());\n            className = cmd.substring(0, cmd.lastIndexOf(NOTE_SYMBOL));\n        }\n    }\n\n}"
  },
  {
    "path": "cola-components/cola-component-test-container/src/test/java/com/alibaba/cola/test/Demo.java",
    "content": "package com.alibaba.cola.test;\n\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\n@Slf4j\npublic class Demo {\n\n    @BeforeEach\n    public void before(){\n        System.out.println(\"before action\");\n    }\n\n    @Test\n    public void testOne(){\n        System.out.println(\"test one\");\n        Assertions.assertEquals(1,1);\n        System.out.println(\"test one end\");\n    }\n\n    @Test\n    public void testTwo(){\n        System.out.println(\"test two\");\n    }\n\n    @Test\n    void testThree(){\n        System.out.println(\"test three\");\n    }\n\n    @Test\n    public void testParam(String param){\n        System.out.println(\"hello param\");\n    }\n\n    @AfterEach\n    public void after(){\n        System.out.println(\"after action\");\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/test/java/com/alibaba/cola/test/DemoWithExtension.java",
    "content": "package com.alibaba.cola.test;\n\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.*;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n\n@SpringBootTest(classes = SpringBootConfig.class)\n@ExtendWith(LoggingExtension.class)\npublic class DemoWithExtension {\n\n    @Autowired\n    private Demo demo;\n\n    @BeforeEach\n    public void before() {\n        System.out.println(\"=====before\");\n    }\n\n    @Test\n    public void testParam(String param) {\n        System.out.println(\"hello : \" + param);\n    }\n\n    @Test\n    public void testMethod1() {\n        System.out.println(\"Begin testMethod1\");\n        demo.testOne();\n        System.out.println(\"End testMethod1\");\n    }\n\n    @Test\n    public void testMethod2() {\n        System.out.println(\"Begin testMethod2\");\n        demo.testTwo();\n        System.out.println(\"End testMethod2\");\n    }\n\n    @AfterEach\n    public void after() {\n        System.out.println(\"=====after\");\n    }\n}\n\n\nclass LoggingExtension implements BeforeEachCallback, ParameterResolver {\n    @Override\n    public void beforeEach(ExtensionContext context) throws Exception {\n        System.out.println(\"Executing test method: \" + context.getRequiredTestMethod().getName());\n    }\n\n    @Override\n    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {\n        return true;\n    }\n\n    @Override\n    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {\n        System.out.println(\"resolveParameter: \" + parameterContext);\n        return null;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/test/java/com/alibaba/cola/test/SpringBootConfig.java",
    "content": "package com.alibaba.cola.test;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SpringBootConfig {\n}\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/test/java/com/alibaba/cola/test/SpringConfig.java",
    "content": "package com.alibaba.cola.test;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * SpringConfig\n *\n * @author Frank Zhang\n * @date 2020-11-17 5:11 PM\n */\n@Configuration\n@ComponentScan\npublic class SpringConfig {\n\n    @Bean(\"demo\")\n    public Demo generateDemo(){\n        return new Demo();\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/test/java/com/alibaba/cola/test/TestsContainerTest.java",
    "content": "package com.alibaba.cola.test;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\n\n/**\n * TestsContainerTest\n *\n * @author Frank Zhang\n * @date 2020-11-17 4:55 PM\n */\npublic class TestsContainerTest {\n    public static void main(String[] args) {\n        TestsContainer.start();\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-test-container/src/test/resources/logback-test.xml",
    "content": "<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n       <encoder>\n           <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>\n           <charset>utf8</charset>\n       </encoder>\n   </appender>\n\n   <!--rootLogger是默认的logger-->\n   <root level=\"INFO\">\n       <!--定义了两个appender，日志会通过往这两个appender里面写-->\n       <appender-ref ref=\"CONSOLE\"/>\n   </root>\n\n   <!--应用日志-->\n   <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n   <logger name=\"com.alibaba.cola.test\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-components/cola-component-unittest/.gitignore",
    "content": "target/\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-components/cola-component-unittest/README.md",
    "content": "## 原理\n通过注解AOP，提供service级别的logging和exception处理。\n\n通过Spring Boot的autoConfig机制进行加载，无需手动配置，只需要添加如下依赖即可：\n```xml\n        <dependency>\n            <groupId>com.alibaba.lst.tech.shared</groupId>\n            <artifactId>catch-log-starter</artifactId>\n        </dependency>\n```\n\n兼容普通的HSF service和Mtop service，具体做法可以查看代码`ResponseHandler`\n```java\npublic class ResponseHandler {\n\n    public static Object handle(Class returnType, String errCode, String errMsg){\n        if (isColaResponse(returnType)){\n            return handleColaResponse(returnType, errCode, errMsg);\n        }\n        if(isMtopResponse(returnType)){\n            return handleMtopResponse(returnType, errCode, errMsg);\n        }\n        return null;\n    }\n    ...\n  }\n\n```\n\n\n## 使用介绍\n1、在需要处理的Service类上面加上@CatchAndLog注解\n```java\n@CatchAndLog\npublic class GrouponServiceImpl implements GrouponService \n```\n\n2、logback-test.xml为组件开启DEGUG level的日志输出\n```xml\n   <!--这个是统一异常处理，日志记录组件的日志-->\n    <logger name=\"com.alibaba.lst.tech.shared\" level=\"DEBUG\"/>\n```\n\n3、如果在控制台看到如下的日志输出，说明CatchAndLog已经在做AOP拦截\n```xml\nDEBUG c.a.l.t.s.catchlog.CatchLogAspect - Start processing: GrouponServiceImpl.queryGrouponItemDetail(..)\nDEBUG c.a.l.t.s.catchlog.CatchLogAspect - REQUEST : 257\n\nDEBUG c.a.l.t.s.catchlog.CatchLogAspect - RESPONSE : {\"errCode\":\"UNKNOWN_ERROR\"...}\nDEBUG c.a.l.t.s.catchlog.CatchLogAspect - COST : 1329ms\n```\n\n"
  },
  {
    "path": "cola-components/cola-component-unittest/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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    <parent>\n        <groupId>com.alibaba.cola</groupId>\n        <artifactId>cola-components-parent</artifactId>\n        <version>5.x-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>cola-component-unittest</artifactId>\n    <packaging>jar</packaging>\n    <name>${project.artifactId}:${project.version}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <properties>\n        <maven.test.skip>true</maven.test.skip>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n        </dependency>\n\n        <!--this is for embedded redis unit test-->\n        <dependency>\n            <groupId>it.ozimov</groupId>\n            <artifactId>embedded-redis</artifactId>\n            <version>0.7.3</version>\n        </dependency>\n        <dependency>\n            <groupId>redis.clients</groupId>\n            <artifactId>jedis</artifactId>\n            <version>5.1.0</version>\n        </dependency>\n\n        <!--this is for embedded database unit test-->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-jpa</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.h2database</groupId>\n            <artifactId>h2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.github.ppodgorsek</groupId>\n            <artifactId>spring-test-dbunit-core</artifactId>\n            <version>5.2.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.dbunit</groupId>\n            <artifactId>dbunit</artifactId>\n            <version>2.7.0</version>\n        </dependency>\n\n        <!--this is for embedded kafka unit test-->\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka-test</artifactId>\n        </dependency>\n\n        <!--this is for microservice unit test, to avoid conflict, we'd better use standalone -->\n        <dependency>\n            <groupId>org.wiremock</groupId>\n            <artifactId>wiremock-standalone</artifactId>\n            <version>3.0.3</version>\n        </dependency>\n        <!--if WebTestClient is used -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n        </dependency>\n\n        <!-- others-->\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-test-container</artifactId>\n            <version>5.x-SNAPSHOT</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.awaitility</groupId>\n            <artifactId>awaitility</artifactId>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/main/java/com/alibaba/cola/unittest/FixtureLoader.java",
    "content": "package com.alibaba.cola.unittest;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.util.StreamUtils;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\n\n@Slf4j\npublic class FixtureLoader {\n\n    public static String loadResource(String resourcePath) {\n        log.info(\"Fixture resource location: \" + resourcePath);\n        // 创建一个 ClassPathResource 对象\n        ClassPathResource resource = new ClassPathResource(resourcePath);\n\n        // 使用 withResource 来自动关闭输入流\n        String content = \"\";\n        try (InputStream inputStream = resource.getInputStream()) {\n            content = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);\n        } catch (IOException e) {\n            e.printStackTrace();\n            throw new RuntimeException(e.getMessage());\n        }\n        return content;\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/main/java/com/alibaba/cola/unittest/kafka/KafkaExtension.java",
    "content": "package com.alibaba.cola.unittest.kafka;\n\nimport com.alibaba.cola.unittest.FixtureLoader;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.node.ObjectNode;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.kafka.common.serialization.Serializer;\nimport org.apache.kafka.common.serialization.StringSerializer;\nimport org.junit.jupiter.api.extension.*;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.kafka.core.DefaultKafkaProducerFactory;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.serializer.JsonSerializer;\nimport org.springframework.kafka.test.EmbeddedKafkaBroker;\nimport org.springframework.kafka.test.utils.KafkaTestUtils;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\n@Slf4j\npublic class KafkaExtension implements BeforeAllCallback, BeforeEachCallback {\n\n    private EmbeddedKafkaBroker embeddedKafkaBroker;\n    private ObjectMapper objectMapper = new ObjectMapper();\n\n    @Override\n    public void beforeAll(ExtensionContext context) throws Exception {\n        try {\n            EmbeddedKafkaBroker embeddedKafkaBroker = (EmbeddedKafkaBroker) SpringExtension.getApplicationContext(context).getBean(EmbeddedKafkaBroker.class);\n            log.debug(\"embeddedKafkaBroker:\" + embeddedKafkaBroker);\n            this.embeddedKafkaBroker = embeddedKafkaBroker;\n        } catch (NoSuchBeanDefinitionException e) {\n            log.error(\"Please add @EmbeddedKafka for your test\", e);\n            throw e;\n        }\n    }\n\n    @Override\n    public void beforeEach(ExtensionContext context) throws Exception {\n        ProduceMessage produceMessage = context.getElement().get().getAnnotation(ProduceMessage.class);\n        if (Objects.nonNull(produceMessage)) {\n            log.info(\"begin produce message for kafka\");\n            String location = produceMessage.value();\n            MessageData messageData = objectMapper.readValue(FixtureLoader.loadResource(location), MessageData.class);\n            log.debug(\"messageData: \" + messageData);\n\n            //get producer\n            KafkaTemplate<String, JsonNode> producer = this.createProducer();\n\n            List<ObjectNode> messages = messageData.getMessages();\n            int count = 0;\n            for (ObjectNode message : messages) {\n                //  TODO：add key support later\n                //  Optional<String> recordKey = Optional.ofNullable(record.remove(\"$KEY$\")).map(JsonNode::asText);\n                producer.send(messageData.getTopic(), message);\n\n                log.info(\"produce message[{}:{}]: {}\", new Object[]{messageData.getTopic(), ++count, message});\n            }\n        }\n    }\n\n    public <K, V> KafkaTemplate<K, V> createProducer(Serializer<K> keySerializer, Serializer<V> valueSerializer) {\n        Map<String, Object> props = KafkaTestUtils.producerProps(this.embeddedKafkaBroker);\n        return new KafkaTemplate(new DefaultKafkaProducerFactory(props, keySerializer, valueSerializer));\n    }\n\n    public <V> KafkaTemplate<String, V> createProducer() {\n        return this.createProducer(new StringSerializer(), new JsonSerializer());\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/main/java/com/alibaba/cola/unittest/kafka/MessageData.java",
    "content": "package com.alibaba.cola.unittest.kafka;\n\nimport com.fasterxml.jackson.databind.node.ObjectNode;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\n\nimport java.util.List;\n\n@Data\n@EqualsAndHashCode\n@ToString\npublic class MessageData {\n    private String topic;\n    private List<ObjectNode> messages;\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/main/java/com/alibaba/cola/unittest/kafka/ProduceMessage.java",
    "content": "package com.alibaba.cola.unittest.kafka;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target( {ElementType.TYPE, ElementType.METHOD})\npublic @interface ProduceMessage {\n\n    String value();\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/main/java/com/alibaba/cola/unittest/redis/ExpectRedis.java",
    "content": "package com.alibaba.cola.unittest.redis;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target( {ElementType.TYPE, ElementType.METHOD})\npublic @interface ExpectRedis {\n    /**\n     * 测试校验数据路径, 通常放在测试夹具（fixture）下面\n     */\n    String value();\n\n    /**\n     * 重试等待key生效的间隔（ms）\n     */\n    long interval() default 200L;\n\n    /**\n     * 验证超时时间（ms）\n     */\n    long timeout() default 3000L;\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/main/java/com/alibaba/cola/unittest/redis/RedisData.java",
    "content": "package com.alibaba.cola.unittest.redis;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Map;\n\n/**\n * 使用jackson：https://zhuanlan.zhihu.com/p/646744855\n */\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class RedisData {\n    // Map<redis key, json content>\n    private Map<String, JsonNode> records;\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/main/java/com/alibaba/cola/unittest/redis/RedisExtension.java",
    "content": "package com.alibaba.cola.unittest.redis;\n\nimport com.alibaba.cola.unittest.FixtureLoader;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.node.JsonNodeType;\nimport com.fasterxml.jackson.databind.node.ObjectNode;\nimport lombok.extern.slf4j.Slf4j;\nimport org.awaitility.Awaitility;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.extension.*;\nimport redis.clients.jedis.Jedis;\nimport redis.embedded.RedisServer;\n\nimport java.time.Duration;\nimport java.util.*;\n\n@Slf4j\npublic class RedisExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback {\n    ObjectMapper objectMapper = new ObjectMapper();\n    //default port is 6397\n    private static RedisServer redisServer;\n    public static Jedis jedis;\n    private static boolean isStarted;\n\n    @Override\n    public void afterEach(ExtensionContext context) throws Exception {\n\n        ExpectRedis expectRedis = context.getElement().get().getAnnotation(ExpectRedis.class);\n        if (Objects.nonNull(expectRedis)) {\n            log.info(\"after each, check redis result\");\n            String location = expectRedis.value();\n            long interval = expectRedis.interval();\n            long timeout = expectRedis.timeout();\n            RedisData redisData = objectMapper.readValue(FixtureLoader.loadResource(location), RedisData.class);\n            redisData.getRecords().forEach((key, content) -> {\n                await(interval, timeout, key);\n                String expect = content.textValue();\n                String actual = jedis.get(key);\n                log.debug(\"expect: \" + expect);\n                log.debug(\"actual: \" + actual);\n                Assertions.assertEquals(expect, actual);\n            });\n        }\n    }\n\n    private void await(long interval, long timeout, String key) {\n        Awaitility.await().pollInterval(Duration.ofMillis(interval)).atMost(Duration.ofMillis(timeout))\n                .until(() -> jedis.exists(key));\n    }\n\n    @Override\n    public void beforeEach(ExtensionContext context) throws Exception {\n        SetupRedis setupRedis = context.getElement().get().getAnnotation(SetupRedis.class);\n        if (Objects.nonNull(setupRedis)) {\n            log.info(\"before each, setup redis\");\n            String location = setupRedis.value();\n            RedisData redisData = objectMapper.readValue(FixtureLoader.loadResource(location), RedisData.class);\n            log.debug(\"redisData: \" + redisData);\n            redisData.getRecords().forEach((key, jsonNode) -> {\n                processJsonNode(key, jsonNode);\n            });\n        }\n    }\n\n    private void processJsonNode(String key, JsonNode jsonNode) {\n        JsonNodeType nodeType = jsonNode.getNodeType();\n        log.debug(\"set redis record: TYPE--> {} , KEY--> {}, VALUE--> {}\", nodeType, key, jsonNode);\n        if (nodeType == JsonNodeType.STRING) {\n            jedis.set(key, jsonNode.textValue());\n            return;\n        }\n        if (nodeType == JsonNodeType.ARRAY) {\n            List<String> elements = new ArrayList<>();\n            jsonNode.forEach(item -> {\n                String itemStr = item.isValueNode() ? item.asText() : item.toString();\n                elements.add(itemStr);\n            });\n            jedis.sadd(key, elements.toArray(new String[0]));\n            return;\n        }\n        if (nodeType == JsonNodeType.OBJECT) {\n            Map<String, String> map = new HashMap<>();\n            ObjectNode objectNode = (ObjectNode) jsonNode;\n            objectNode.fields()\n                    .forEachRemaining(\n                            field -> {\n                                String value = field.getValue().isValueNode() ? field.getValue().asText() : field.getValue().toString();\n                                map.put(field.getKey(), value);\n                            });\n            jedis.hmset(key, map);\n        }\n    }\n\n    @Override\n    public void beforeAll(ExtensionContext context) {\n        try {\n            if (redisServer == null && !isStarted) {\n                redisServer = new RedisServer(); //default port is 6379\n                redisServer.start();\n                log.debug(\"Redis server started\");\n            }\n        } catch (Exception e) {\n            isStarted = true;\n            log.warn(\"Redis Server may already started, just ignore this exception:\" + e.getMessage());\n        }\n        if (jedis == null) {\n            jedis = new Jedis(\"localhost\", 6379);\n        }\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/main/java/com/alibaba/cola/unittest/redis/SetupRedis.java",
    "content": "package com.alibaba.cola.unittest.redis;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 测试启动时注入redis记录\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target( {ElementType.TYPE, ElementType.METHOD})\npublic @interface SetupRedis {\n    /**\n     * 测试准备数据路径, 通常放在测试夹具（fixture）下面，比如：/fixture/job.json\n     */\n    String value();\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/main/java/com/alibaba/cola/unittest/wiremock/WireMockRegister.java",
    "content": "package com.alibaba.cola.unittest.wiremock;\n\nimport com.alibaba.cola.unittest.FixtureLoader;\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wiremock.stubbing.StubMapping;\n\npublic class WireMockRegister {\n\n    public static void registerStub(WireMock wireMock, String resourcePath){\n        StubMapping stubMapping = StubMapping.buildFrom(FixtureLoader.loadResource(resourcePath));\n        wireMock.register(stubMapping);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/java/com/alibaba/cola/unittest/Application.java",
    "content": "package com.alibaba.cola.unittest;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * Application\n *\n * @author Frank Zhang\n * @date 2020-11-10 3:58 PM\n */\n@SpringBootApplication(scanBasePackages = {\"com.alibaba.cola.unittest\"})\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/java/com/alibaba/cola/unittest/TestsContainerBoot.java",
    "content": "package com.alibaba.cola.unittest;\n\nimport com.alibaba.cola.test.TestsContainer;\n\npublic class TestsContainerBoot {\n    public static void main(String[] args) {\n        TestsContainer.start();\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/java/com/alibaba/cola/unittest/db/DBSetupTest.java",
    "content": "package com.alibaba.cola.unittest.db;\n\nimport com.github.springtestdbunit.DbUnitTestExecutionListener;\nimport com.github.springtestdbunit.annotation.DatabaseSetup;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.TestExecutionListeners;\nimport org.springframework.test.context.support.DependencyInjectionTestExecutionListener;\n\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n// 关于TestExecutionListener： https://www.baeldung.com/spring-testexecutionlistener\n@SpringBootTest\n@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DbUnitTestExecutionListener.class })\npublic class DBSetupTest {\n\n    @Autowired\n    private PersonRepository personRepository;\n\n    @Test\n    @DatabaseSetup(\"/fixture/db/sample-data.xml\")\n    public void testFind() throws Exception {\n        List<Person> personList = personRepository.find(\"hil\");\n        System.out.println(personList);\n        assertEquals(1, personList.size());\n        assertEquals(\"Phillip\", personList.get(0).getFirstName());\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/java/com/alibaba/cola/unittest/db/Person.java",
    "content": "package com.alibaba.cola.unittest.db;\n\nimport jakarta.persistence.Entity;\nimport jakarta.persistence.Id;\nimport jakarta.persistence.NamedQueries;\nimport jakarta.persistence.NamedQuery;\n\n@Entity\n@NamedQueries({ @NamedQuery(name = \"Person.find\", query = \"SELECT p from Person p where p.firstName like :name \"\n        + \"or p.lastName like :name\") })\npublic class Person {\n\n    @Id\n    private int id;\n\n    private String title;\n\n    private String firstName;\n\n    private String lastName;\n\n    public int getId() {\n        return id;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public void setTitle(String title) {\n        this.title = title;\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    @Override\n    public String toString() {\n        return \"Person{\" +\n                \"id=\" + id +\n                \", title='\" + title + '\\'' +\n                \", firstName='\" + firstName + '\\'' +\n                \", lastName='\" + lastName + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/java/com/alibaba/cola/unittest/db/PersonRepository.java",
    "content": "package com.alibaba.cola.unittest.db;\n\nimport jakarta.persistence.EntityManager;\nimport jakarta.persistence.PersistenceContext;\nimport jakarta.persistence.Query;\nimport jakarta.transaction.Transactional;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.List;\n\n@Repository\n@Transactional\npublic class PersonRepository {\n\n    @PersistenceContext\n    private EntityManager entityManager;\n\n    @SuppressWarnings(\"unchecked\")\n    public List<Person> find(String name) {\n        Query query = entityManager.createNamedQuery(\"Person.find\");\n        query.setParameter(\"name\", \"%\" + name + \"%\");\n        return query.getResultList();\n    }\n\n    public void remove(int personId) {\n        Person person = entityManager.find(Person.class, personId);\n        entityManager.remove(person);\n    }\n\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/java/com/alibaba/cola/unittest/kafka/KafkaConsumer.java",
    "content": "package com.alibaba.cola.unittest.kafka;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.kafka.clients.consumer.ConsumerRecord;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.stereotype.Component;\n\n@Slf4j\n@Component\npublic class KafkaConsumer {\n\n\n    private String payload;\n    boolean isFinished;\n\n    @KafkaListener(topics = \"${test.topic}\", groupId = \"testGroup\")\n    public void receive(ConsumerRecord<?, ?> consumerRecord) {\n        log.info(\"received payload='{}'\", consumerRecord.toString());\n        payload = consumerRecord.toString();\n\n        processBiz();\n\n        isFinished = true;\n    }\n\n    private void processBiz() {\n        try {\n            Thread.sleep(300);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n\n    public String getPayload() {\n        return payload;\n    }\n\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/java/com/alibaba/cola/unittest/kafka/KafkaExtensionTest.java",
    "content": "package com.alibaba.cola.unittest.kafka;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.kafka.test.context.EmbeddedKafka;\n\nimport java.util.concurrent.TimeUnit;\n\nimport static org.awaitility.Awaitility.await;\n\n@Slf4j\n@SpringBootTest\n@EmbeddedKafka(partitions = 1, brokerProperties = { \"listeners=PLAINTEXT://localhost:9092\", \"port=9092\" })\n@ExtendWith(KafkaExtension.class)\npublic class KafkaExtensionTest {\n\n    @Autowired\n    private KafkaConsumer consumer;\n\n    @Test\n    @ProduceMessage(\"/fixture/kafka/produce-message.json\")\n    public void testProduceMessage(){\n        log.info(\"test produce message\");\n\n        // 等待消息业务处理，每100毫秒poll一下，最长等待10秒\n        await().atMost(10, TimeUnit.SECONDS).pollInterval(100, TimeUnit.MILLISECONDS)\n                .until(() -> consumer.isFinished);\n\n        log.info(\"consume message finished\");\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/java/com/alibaba/cola/unittest/redis/RedisExtensionTest.java",
    "content": "package com.alibaba.cola.unittest.redis;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport java.util.Map;\nimport java.util.Set;\n\n@ExtendWith(RedisExtension.class)\npublic class RedisExtensionTest {\n\n    /**\n     * json String 的内容，注意如果是String的话，需要用转义符号\\，转义双引号\n     * {\n     * \"records\": {\n     * \"topo:child_job:222\": \"{\\\"id\\\":\\\"12345678-1234-1234-1234-childJob0000\\\",\\\"parent_job_id\\\":\\\"12345678-1234-1234-1234-noParentJob0\\\",\\\"resource_id\\\":\\\"1.1.1.4\\\",\\\"status\\\":\\\"success\\\"}\",\n     * \"topo:child_job:333\": \"{\\\"id\\\":\\\"12345678-1234-1234-1234-childJob0000\\\"}\"\n     * }\n     * }\n     */\n    @Test\n    @SetupRedis(\"/fixture/redis/string-setup.json\")\n    public void testString() {\n        System.out.println(\"test String SetupRedis\");\n    }\n\n    /**\n     * json array 的内容：\n     * {\n     * \"records\": {\n     * \"xlink:10.0.0.11:port\": [\n     * \"30000000-0000-0000-0000-000000000001\",\n     * \"30000000-0000-0000-0000-000000000002\"\n     * ]\n     * }\n     * }\n     */\n    @Test\n    @SetupRedis(\"/fixture/redis/array-setup.json\")\n    public void testArray() {\n        System.out.println(\"test array SetupRedis\");\n        Set<String> result = RedisExtension.jedis.smembers(\"test:array\");\n        System.out.println(\"test result : \" + result);\n        Assertions.assertEquals(2, result.size());\n    }\n\n    /**\n     * json 的object在redis里面是用hash存储，内容如下：\n     * {\n     * \"records\": {\n     * \"xlink:hyper_cluster_port:30000000-0000-0000-0000-000000000001\": {\n     * \"version\": 1,\n     * \"json\": {\n     * \"id\": \"30000000-0000-0000-0000-000000000001\",\n     * \"name\": \"port-01\",\n     * \"project_id\": \"7a9941d34fc1497d8d0797429ecfd354\",\n     * \"provisioning_status\": \"active\",\n     * \"created_at\": \"2024-01-01T12:00:00Z\",\n     * \"updated_at\": \"2024-01-01T12:00:00Z\"\n     * }\n     * }\n     * }\n     * }\n     */\n    @Test\n    @SetupRedis(\"/fixture/redis/hash-setup.json\")\n    public void testHash() {\n        System.out.println(\"test hash SetupRedis\");\n        Map<String, String> result = RedisExtension.jedis.hgetAll(\"test:hash\");\n        System.out.println(\"test result : \" + result);\n        Assertions.assertEquals(\"1\", result.get(\"version\"));\n    }\n\n    @Test\n    @SetupRedis(\"/fixture/redis/string-setup.json\")\n    @ExpectRedis(\"/fixture/redis/string-expect.json\")\n    public void testStringExpect() {\n        System.out.println(\"test ExpectRedis\");\n    }\n\n    @Test\n    public void testVoid() {\n        System.out.println(\"test without SetupRedis\");\n    }\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/java/com/alibaba/cola/unittest/wiremock/Account.java",
    "content": "package com.alibaba.cola.unittest.wiremock;\n\nimport lombok.Data;\n\n@Data\npublic class Account {\n    /**\n     * 用户号码\n     */\n    private long phoneNo;\n\n    /**\n     * 账户余额\n     */\n    private String remaining;\n\n\n    private String name;\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/java/com/alibaba/cola/unittest/wiremock/WireMockBasicTest.java",
    "content": "package com.alibaba.cola.unittest.wiremock;\n\nimport com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;\nimport com.github.tomakehurst.wiremock.junit5.WireMockTest;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.http.MediaType;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\n\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@WireMockTest(httpPort = 8080)\n@Slf4j\npublic class WireMockBasicTest {\n\n    @Autowired\n    protected WebTestClient webClient;\n\n    @Test\n    public void testWireMockBasic() {\n        // The static DSL will be automatically configured for you\n        stubFor(get(\"/static-dsl\").willReturn(ok()));\n\n        webClient.get()\n                .uri(\"http://localhost:8080/static-dsl\")\n                .exchange()\n                .expectStatus()\n                .isEqualTo(200);\n    }\n\n    @Test\n    public void testWireMockStub(WireMockRuntimeInfo wmRuntimeInfo) {\n        WireMockRegister.registerStub(wmRuntimeInfo.getWireMock(), \"/fixture/wiremock/stub-wire-mock-basic.json\");\n\n        webClient.get()\n                .uri(\"http://localhost:8080/v1/wiremock/basic\")\n                .exchange()\n                .expectStatus()\n                .isEqualTo(200)\n                .expectHeader()\n                .contentType(MediaType.APPLICATION_JSON);\n\n        System.out.println(\"wire mock serer port : \" + wmRuntimeInfo.getHttpPort());\n    }\n\n    @Test\n    public void testWireMockAccount(WireMockRuntimeInfo wmRuntimeInfo) {\n        WireMockRegister.registerStub(wmRuntimeInfo.getWireMock(), \"/fixture/wiremock/stub-account.json\");\n\n        long phoneNo = 123456789;\n\n        webClient.get()\n                .uri(\"http://localhost:8080/v1/api/account/\"+phoneNo)\n                .exchange()\n                .expectStatus()\n                .isEqualTo(200)\n                .expectHeader()\n                .contentType(MediaType.APPLICATION_JSON)\n                .returnResult(Account.class)\n                .getResponseBody()\n                .map(account -> {\n                    log.info(account.toString());\n                    Assertions.assertEquals(\"frank\", account.getName());\n                    Assertions.assertEquals(phoneNo, account.getPhoneNo());\n                    return account;\n                })\n                .subscribe();\n\n        log.info(\"wire mock serer port : \" + wmRuntimeInfo.getHttpPort());\n    }\n\n}\n\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/resources/application.properties",
    "content": "test.topic=embedded-test-topic\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/resources/fixture/db/sample-data.xml",
    "content": "<?xml version='1.0' encoding='UTF-8'?>\n\n<dataset>\n    <Person id=\"0\" title=\"Mr\" first_name=\"Phillip\" last_name=\"Webb\"/>\n    <Person id=\"1\" title=\"Mr\" first_name=\"Mario\" last_name=\"Zagar\"/>\n</dataset>\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/resources/fixture/kafka/produce-message.json",
    "content": "{\n  \"topic\": \"embedded-test-topic\",\n  \"messages\": [\n    {\n      \"job_id\": \"10000000-0000-0000-0000-000000000001\",\n      \"version\": \"v1\",\n      \"action\": \"create\",\n      \"resource_type\": \"test_resource\",\n      \"request\": [\n        {\n          \"id\": \"30000000-0000-0000-0000-000000000001\",\n          \"name\": \"test-01\",\n          \"project_id\": \"7a9941d34fc1497d8d0797429ecfd354\"\n        }\n      ]\n    }\n  ,\n    {\n      \"job_id\": \"10000000-0000-0000-0000-000000000002\",\n      \"version\": \"v1\",\n      \"action\": \"create\",\n      \"resource_type\": \"test_resource\",\n      \"request\": [\n        {\n          \"id\": \"30000000-0000-0000-0000-000000000002\",\n          \"name\": \"test-02\",\n          \"project_id\": \"7a9941d34fc1497d8d0797429ecfd354\"\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/resources/fixture/redis/array-setup.json",
    "content": "{\n  \"records\": {\n    \"test:array\": [\n      \"30000000-0000-0000-0000-000000000001\",\n      \"30000000-0000-0000-0000-000000000002\"\n    ]\n  }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/resources/fixture/redis/hash-setup.json",
    "content": "{\n  \"records\": {\n    \"test:hash\": {\n      \"version\": 1,\n      \"json\": {\n        \"id\": \"30000000-0000-0000-0000-000000000001\",\n        \"name\": \"port-01\",\n        \"project_id\": \"7a9941d34fc1497d8d0797429ecfd354\",\n        \"provisioning_status\": \"active\",\n        \"created_at\": \"2024-01-01T12:00:00Z\",\n        \"updated_at\": \"2024-01-01T12:00:00Z\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/resources/fixture/redis/string-expect.json",
    "content": "{\n  \"records\": {\n    \"topo:child_job:222\": \"{\\\"id\\\":\\\"12345678-1234-1234-1234-childJob0000\\\",\\\"parent_job_id\\\":\\\"12345678-1234-1234-1234-noParentJob0\\\",\\\"resource_id\\\":\\\"1.1.1.4\\\",\\\"status\\\":\\\"success\\\"}\",\n    \"topo:child_job:333\": \"{\\\"id\\\":\\\"12345678-1234-1234-1234-childJob0000\\\"}\"\n\n  }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/resources/fixture/redis/string-setup.json",
    "content": "{\n  \"records\": {\n    \"topo:child_job:222\": \"{\\\"id\\\":\\\"12345678-1234-1234-1234-childJob0000\\\",\\\"parent_job_id\\\":\\\"12345678-1234-1234-1234-noParentJob0\\\",\\\"resource_id\\\":\\\"1.1.1.4\\\",\\\"status\\\":\\\"success\\\"}\",\n    \"topo:child_job:333\": \"{\\\"id\\\":\\\"12345678-1234-1234-1234-childJob0000\\\"}\"\n\n  }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/resources/fixture/wiremock/stub-account.json",
    "content": "{\n  \"request\": {\n    \"urlPathPattern\": \"/v1/api/account/[0-9]+\",\n    \"method\": \"GET\"\n  },\n  \"response\": {\n    \"status\": 200,\n    \"headers\": {\n      \"Content-Type\": \"application/json\"\n    },\n    \"transformers\": [\n      \"response-template\"\n    ],\n    \"jsonBody\": {\n      \"name\": \"frank\",\n      \"phoneNo\": \"{{request.path.[3]}}\",\n      \"remaining\": \"400\",\n      \"chargePlanList\": [\n        {\n          \"priority\": \"2\",\n          \"type\": \"fixedTime\"\n        },\n        {\n          \"priority\": \"1\",\n          \"type\": \"familyMember\"\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/resources/fixture/wiremock/stub-wire-mock-basic.json",
    "content": "{\n  \"request\": {\n    \"urlPathPattern\": \"/v1/wiremock/basic\",\n    \"method\": \"GET\"\n  },\n  \"response\": {\n    \"status\": 200,\n    \"headers\": {\n      \"Content-Type\": \"application/json\"\n    },\n    \"jsonBody\": {\n      \"request_id\": \"f7f9e747-f073-4ea8-8360-b42fc754a049\",\n      \"test_resource\": {\n        \"name\": \"1520-001\",\n        \"created_at\": \"2024-02-04 15:11:13\",\n        \"updated_at\": \"2020-02-04 15:11:13\",\n        \"type\": \"l1\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "cola-components/cola-component-unittest/src/test/resources/logback-test.xml",
    "content": "<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%date{HH:mm:ss} %highlight(%-5level) [%blue(%t)] %yellow(%C{35}): %msg%n%throwable</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <!--定义了两个appender，日志会通过往这两个appender里面写-->\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n\n    <!--应用日志-->\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n    <logger name=\"com.alibaba.cola.unittest\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-components/cola-components-bom/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         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    <!--\n        Maven BOM Template\n        https://github.com/anliksim/maven-template-bom\n    -->\n    <groupId>com.alibaba.cola</groupId>\n    <artifactId>cola-components-bom</artifactId>\n    <version>5.x-SNAPSHOT</version>\n    <packaging>pom</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>com.alibaba.cola</groupId>\n                <artifactId>cola-component-dto</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cola</groupId>\n                <artifactId>cola-component-exception</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cola</groupId>\n                <artifactId>cola-component-statemachine</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cola</groupId>\n                <artifactId>cola-component-domain-starter</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cola</groupId>\n                <artifactId>cola-component-extension-starter</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cola</groupId>\n                <artifactId>cola-component-catchlog-starter</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cola</groupId>\n                <artifactId>cola-component-test-container</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <distributionManagement>\n        <snapshotRepository>\n            <id>ossrh</id>\n            <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n        </snapshotRepository>\n    </distributionManagement>\n\n    <build>\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <artifactId>maven-source-plugin</artifactId>\n                    <version>3.3.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-javadoc-plugin</artifactId>\n                    <version>3.7.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-deploy-plugin</artifactId>\n                    <version>3.1.1</version>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n    </build>\n\n    <profiles>\n        <profile>\n            <id>gen-sign</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <artifactId>maven-gpg-plugin</artifactId>\n                        <version>3.1.0</version>\n                        <executions>\n                            <execution>\n                                <id>sign-artifacts</id>\n                                <phase>verify</phase>\n                                <goals>\n                                    <goal>sign</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>deploy-settings</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.sonatype.plugins</groupId>\n                        <artifactId>nexus-staging-maven-plugin</artifactId>\n                        <version>1.6.13</version>\n                        <extensions>true</extensions>\n                        <configuration>\n                            <serverId>ossrh</serverId>\n                            <nexusUrl>https://oss.sonatype.org/</nexusUrl>\n                            <autoReleaseAfterClose>true</autoReleaseAfterClose>\n                        </configuration>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n    </profiles>\n</project>\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/README.md",
    "content": "# COLA Dev Util Archetypes\n\n用于开发时快速生成`COLA Components`工程的Archetypes，即方便COLA自身开发的工具工程。\n\n提供了脚本，调用`Util Archetypes`生成`COLA Components`工程：\n\n- [`new-cola-normal-component.sh`](new-cola-normal-component.sh)\n- [`new-cola-starter-component.sh`](new-cola-starter-component.sh)\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-normal-component-archetype/.gitignore",
    "content": "target/\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-normal-component-archetype/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.alibaba.cola</groupId>\n    <artifactId>cola-normal-component-archetype</artifactId>\n    <version>1.0.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n    <name>cola-normal-component-archetype</name>\n    <description>COLA normal component archetype</description>\n\n    <build>\n        <extensions>\n            <extension>\n                <groupId>org.apache.maven.archetype</groupId>\n                <artifactId>archetype-packaging</artifactId>\n                <version>3.0.1</version>\n            </extension>\n        </extensions>\n\n        <plugins>\n            <plugin>\n                <artifactId>maven-source-plugin</artifactId>\n                <version>3.3.1</version>\n            </plugin>\n\n            <plugin>\n                <artifactId>maven-archetype-plugin</artifactId>\n                <version>3.0.1</version>\n            </plugin>\n\n            <plugin>\n                <artifactId>maven-resources-plugin</artifactId>\n                <version>3.0.1</version>\n                <configuration>\n                    <addDefaultExcludes>false</addDefaultExcludes>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-normal-component-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archetype-descriptor\n    xsi:schemaLocation=\"https://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0 http://maven.apache.org/xsd/archetype-descriptor-1.1.0.xsd\"\n    name=\"dto\"\n    xmlns=\"https://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n    <fileSets>\n        <fileSet>\n            <directory></directory>\n            <includes>\n                <include>gitignore.txt</include>\n            </includes>\n        </fileSet>\n\n        <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n            <directory>src/main/java</directory>\n            <includes>\n                <include>**/*.java</include>\n            </includes>\n        </fileSet>\n        <fileSet filtered=\"true\" encoding=\"UTF-8\">\n            <directory>src/main/resources</directory>\n            <includes>\n                <include>**/*.xml</include>\n                <include>**/*.properties</include>\n            </includes>\n        </fileSet>\n        <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n            <directory>src/test/java</directory>\n            <includes>\n                <include>**/*.java</include>\n            </includes>\n        </fileSet>\n        <fileSet filtered=\"true\" encoding=\"UTF-8\">\n            <directory>src/test/resources</directory>\n            <includes>\n                <include>**/*.xml</include>\n                <include>**/*.properties</include>\n            </includes>\n        </fileSet>\n    </fileSets>\n</archetype-descriptor>\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-normal-component-archetype/src/main/resources/archetype-resources/gitignore.txt",
    "content": "target/\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-normal-component-archetype/src/main/resources/archetype-resources/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>cola-components-parent</artifactId>\n        <version>${version}</version>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n            <scope>provided</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-normal-component-archetype/src/main/resources/archetype-resources/src/main/java/Dummy.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package};\n\n/**\n *\n * Dummy class\n *\n * @author Frank Zhang\n */\npublic abstract class Dummy{\n\n    private static final long serialVersionUID = 1L;\n\n}\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-normal-component-archetype/src/main/resources/archetype-resources/src/test/resources/logback-test.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n       <encoder>\n           <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>\n           <charset>utf8</charset>\n       </encoder>\n   </appender>\n\n   <!--rootLogger是默认的logger-->\n   <root level=\"INFO\">\n       <!--定义了两个appender，日志会通过往这两个appender里面写-->\n       <appender-ref ref=\"CONSOLE\"/>\n   </root>\n\n   <!--应用日志-->\n   <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n   <logger name=\"${package}\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-normal-component-archetype/src/test/resources/projects/basic/archetype.properties",
    "content": "#Fri Nov 13 12:26:43 CST 2020\npackage=it.pkg\nversion=0.1-SNAPSHOT\ngroupId=archetype.it\nartifactId=basic\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-normal-component-archetype/src/test/resources/projects/basic/goal.txt",
    "content": ""
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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.alibaba.cola</groupId>\n    <artifactId>cola-starter-component-archetype</artifactId>\n    <version>1.0.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n    <name>cola-starter-component-archetype</name>\n\n    <build>\n        <extensions>\n            <extension>\n                <groupId>org.apache.maven.archetype</groupId>\n                <artifactId>archetype-packaging</artifactId>\n                <version>3.2.0</version>\n            </extension>\n        </extensions>\n\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <artifactId>maven-archetype-plugin</artifactId>\n                    <version>3.2.0</version>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n    </build>\n</project>\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archetype-descriptor\n    xsi:schemaLocation=\"https://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0 http://maven.apache.org/xsd/archetype-descriptor-1.1.0.xsd\"\n    name=\"catch-log-starter\"\n    xmlns=\"https://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n    <fileSets>\n        <fileSet>\n            <directory></directory>\n            <includes>\n                <include>gitignore.txt</include>\n                <include>README.md</include>\n            </includes>\n        </fileSet>\n        <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n            <directory>src/main/java</directory>\n            <includes>\n                <include>**/*.java</include>\n            </includes>\n        </fileSet>\n        <fileSet filtered=\"true\" encoding=\"UTF-8\">\n            <directory>src/main/resources</directory>\n            <includes>\n                <include>**/*.factories</include>\n            </includes>\n        </fileSet>\n        <fileSet filtered=\"true\" packaged=\"true\" encoding=\"UTF-8\">\n            <directory>src/test/java</directory>\n            <includes>\n                <include>**/*.java</include>\n            </includes>\n        </fileSet>\n        <fileSet filtered=\"true\" encoding=\"UTF-8\">\n            <directory>src/test/resources</directory>\n            <includes>\n                <include>**/*.xml</include>\n                <include>**/*.properties</include>\n            </includes>\n        </fileSet>\n    </fileSets>\n</archetype-descriptor>\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/main/resources/archetype-resources/README.md",
    "content": "## 原理\n通过注解AOP，提供service级别的logging和exception处理。\n\n通过Spring Boot的autoConfig机制进行加载，无需手动配置，只需要添加如下依赖即可：\n```xml\n        <dependency>\n            <groupId>com.alibaba.lst.tech.shared</groupId>\n            <artifactId>catch-log-starter</artifactId>\n        </dependency>\n```\n\n兼容普通的HSF service和Mtop service，具体做法可以查看代码`ResponseHandler`\n```java\npublic class ResponseHandler {\n\n    public static Object handle(Class returnType, String errCode, String errMsg){\n        if (isColaResponse(returnType)){\n            return handleColaResponse(returnType, errCode, errMsg);\n        }\n        if(isMtopResponse(returnType)){\n            return handleMtopResponse(returnType, errCode, errMsg);\n        }\n        return null;\n    }\n    ...\n  }\n\n```\n\n\n## 使用介绍\n1、在需要处理的Service类上面加上@CatchAndLog注解\n```java\n@CatchAndLog\npublic class GrouponServiceImpl implements GrouponService \n```\n\n2、logback-test.xml为组件开启DEGUG level的日志输出\n```xml\n   <!--这个是统一异常处理，日志记录组件的日志-->\n    <logger name=\"com.alibaba.lst.tech.shared\" level=\"DEBUG\"/>\n```\n\n3、如果在控制台看到如下的日志输出，说明CatchAndLog已经在做AOP拦截\n```xml\nDEBUG c.a.l.t.s.catchlog.CatchLogAspect - Start processing: GrouponServiceImpl.queryGrouponItemDetail(..)\nDEBUG c.a.l.t.s.catchlog.CatchLogAspect - REQUEST : 257\n\nDEBUG c.a.l.t.s.catchlog.CatchLogAspect - RESPONSE : {\"errCode\":\"UNKNOWN_ERROR\"...}\nDEBUG c.a.l.t.s.catchlog.CatchLogAspect - COST : 1329ms\n```\n\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/main/resources/archetype-resources/gitignore.txt",
    "content": "target/\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\nout/\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nbin/\ndoc/\n.DS_Store\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/main/resources/archetype-resources/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 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    <parent>\n        <groupId>${groupId}</groupId>\n        <artifactId>cola-components-parent</artifactId>\n        <version>${version}</version>\n    </parent>\n\n    <artifactId>${artifactId}</artifactId>\n    <packaging>jar</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-autoconfigure</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-configuration-processor</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- 工具包 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- 日志包 -->\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n        </dependency>\n\n        <!-- 测试包 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/main/resources/archetype-resources/src/main/java/CatchAndLog.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package};\n\n/**\n * CatchAndLog\n *\n * @author Frank Zhang\n * @date 2020-11-10 10:48 AM\n */\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target({ElementType.METHOD, ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface CatchAndLog {\n\n}\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/main/resources/archetype-resources/src/main/java/CatchLogAspect.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package};\n\nimport com.alibaba.fastjson.JSON;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Pointcut;\nimport org.aspectj.lang.reflect.MethodSignature;\n\n/**\n * @ Description   :  Catching and Logging\n * @ Author        :  Frank Zhang\n * @ CreateDate    :  2020/11/09\n * @ Version       :  1.0\n */\n@Aspect\n@Slf4j\npublic class CatchLogAspect {\n\n    /**\n     * The syntax of pointcut : https://blog.csdn.net/zhengchao1991/article/details/53391244\n     */\n    @Pointcut(\"@within(CatchAndLog) && execution(public * *(..))\")\n    public void pointcut(){\n    }\n\n    @Around(value = \"pointcut()\")\n    public Object around(ProceedingJoinPoint joinPoint ) {\n        long startTime =  System.currentTimeMillis();\n\n        logRequest(joinPoint);\n\n        Object response = null;\n        try {\n             response = joinPoint.proceed();\n        }\n        catch (Throwable e){\n            response = handleException(joinPoint, e);\n        }\n        finally {\n            logResponse(startTime, response);\n        }\n\n        return response ;\n    }\n\n    private Object handleException(ProceedingJoinPoint joinPoint, Throwable e) {\n        MethodSignature ms = (MethodSignature)joinPoint.getSignature();\n        Class returnType = ms.getReturnType();\n        //Dummy implementation\n        return null;\n    }\n\n\n    private void logResponse(long startTime, Object response) {\n        try{\n            long endTime =  System.currentTimeMillis();\n            log.debug(\"RESPONSE : \"+ JSON.toJSONString(response) );\n            log.debug(\"COST : \" + (endTime - startTime) + \"ms\");\n        }\n        catch (Exception e){\n            //swallow it\n            log.error(\"logResponse error : \" + e);\n        }\n    }\n\n    private void logRequest(ProceedingJoinPoint joinPoint) {\n        try {\n            log.debug(\"START PROCESSING: \" + joinPoint.getSignature().toShortString());\n            Object[] args = joinPoint.getArgs();\n            for (Object arg : args) {\n                log.debug(\"REQUEST : \" + JSON.toJSONString(arg));\n            }\n        }\n        catch (Exception e){\n            //swallow it\n            log.error(\"logReqeust error : \" + e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/main/resources/archetype-resources/src/main/java/CatchLogAutoConfiguration.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package};\n\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n/**\n * @ Description   :\n * @ Author        :  Frank Zhang\n * @ CreateDate    :  2020/11/09\n * @ Version       :  1.0\n */\n@Configuration\n@EnableAspectJAutoProxy\npublic class CatchLogAutoConfiguration {\n\n    @Bean\n    @ConditionalOnMissingBean(CatchLogAspect.class)\n    public CatchLogAspect initCatchLogAspect() {\n        return new CatchLogAspect();\n    }\n}\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/main/resources/archetype-resources/src/main/resources/META-INF/spring.factories",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\norg.springframework.boot.autoconfigure.EnableAutoConfiguration = ${package}.CatchLogAutoConfiguration\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/main/resources/archetype-resources/src/test/java/test/Application.java",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\npackage ${package}.test;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * Application\n *\n * @author Frank Zhang\n * @date 2020-11-10 3:58 PM\n */\n@SpringBootApplication(scanBasePackages = {\"${package}\"})\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/main/resources/archetype-resources/src/test/resources/application.properties",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/main/resources/archetype-resources/src/test/resources/logback-test.xml",
    "content": "#set( $symbol_pound = '#' )\n#set( $symbol_dollar = '$' )\n#set( $symbol_escape = '\\' )\n<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <!--定义了两个appender，日志会通过往这两个appender里面写-->\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n\n    <!--应用日志-->\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n    <logger name=\"${package}\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/test/resources/projects/basic/archetype.properties",
    "content": "#Fri Nov 13 16:34:48 CST 2020\npackage=it.pkg\nversion=0.1-SNAPSHOT\ngroupId=archetype.it\nartifactId=basic\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/cola-starter-component-archetype/src/test/resources/projects/basic/goal.txt",
    "content": ""
  },
  {
    "path": "cola-components/dev-util-archetypes/new-cola-normal-component.sh",
    "content": "#!/bin/bash\nset -eEuo pipefail\n# adjust current dir to script dir\ncd \"$(dirname \"$(readlink -f \"$0\")\")\"\n\nsource ../../scripts/common.sh\nsource ../../scripts/common_build.sh\n\n# shellcheck disable=SC2154\n[ $# -ne 1 ] && die \"need only 1 argument for component name!$nl${nl}usage:$nl  $0 hello\"\nreadonly component_name=\"$1\"\nreadonly archetype_dir=cola-normal-component-archetype\n\n(\n    cd \"$archetype_dir\"\n    MVN_WITH_BASIC_OPTIONS install\n)\n\ngroupId=$(extractFirstElementValueFromPom groupId ../pom.xml)\ncomponent_version=$(extractFirstElementValueFromPom version ../pom.xml)\n\narchetypeGroupId=$(extractFirstElementValueFromPom groupId \"$archetype_dir/pom.xml\")\narchetypeArtifactId=$(extractFirstElementValueFromPom artifactId \"$archetype_dir/pom.xml\")\narchetypeVersion=$(extractFirstElementValueFromPom version \"$archetype_dir/pom.xml\")\n\ncd ..\n\nMVN_WITH_BASIC_OPTIONS archetype:generate \\\n    -DgroupId=\"$groupId\" \\\n    -DartifactId=\"cola-component-$component_name\" \\\n    -Dversion=\"$component_version\" \\\n    -Dpackage=\"com.alibaba.cola.$component_name\" \\\n    -DarchetypeGroupId=\"$archetypeGroupId\" \\\n    -DarchetypeArtifactId=\"$archetypeArtifactId\" \\\n    -DarchetypeVersion=\"$archetypeVersion\" \\\n    -DinteractiveMode=false \\\n    -DarchetypeCatalog=local\n"
  },
  {
    "path": "cola-components/dev-util-archetypes/new-cola-starter-component.sh",
    "content": "#!/bin/bash\nset -eEuo pipefail\n# adjust current dir to script dir\ncd \"$(dirname \"$(readlink -f \"$0\")\")\"\n\nsource ../../scripts/common.sh\nsource ../../scripts/common_build.sh\n\n# shellcheck disable=SC2154\n[ $# -ne 1 ] && die \"need only 1 argument for component name!$nl${nl}usage:$nl  $0 hello\"\nreadonly component_name=\"$1\"\nreadonly archetype_dir=cola-starter-component-archetype\n\n(\n    cd \"$archetype_dir\"\n    MVN_WITH_BASIC_OPTIONS install\n)\n\ngroupId=$(extractFirstElementValueFromPom groupId ../pom.xml)\ncomponent_version=$(extractFirstElementValueFromPom version ../pom.xml)\n\narchetypeGroupId=$(extractFirstElementValueFromPom groupId \"$archetype_dir/pom.xml\")\narchetypeArtifactId=$(extractFirstElementValueFromPom artifactId \"$archetype_dir/pom.xml\")\narchetypeVersion=$(extractFirstElementValueFromPom version \"$archetype_dir/pom.xml\")\n\ncd ..\nMVN_WITH_BASIC_OPTIONS archetype:generate \\\n    -DgroupId=\"$groupId\" \\\n    -DartifactId=\"cola-component-$component_name-starter\" \\\n    -Dversion=\"$component_version\" \\\n    -Dpackage=\"com.alibaba.cola.$component_name\" \\\n    -DarchetypeGroupId=\"$archetypeGroupId\" \\\n    -DarchetypeArtifactId=\"$archetypeArtifactId\" \\\n    -DarchetypeVersion=\"$archetypeVersion\" \\\n    -DinteractiveMode=false \\\n    -DarchetypeCatalog=local\n"
  },
  {
    "path": "cola-components/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<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/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.alibaba.cola</groupId>\n    <artifactId>cola-components-parent</artifactId>\n    <version>5.x-SNAPSHOT</version>\n    <packaging>pom</packaging>\n    <name>${project.artifactId}</name>\n    <description>${project.artifactId}</description>\n    <url>https://github.com/alibaba/COLA</url>\n\n    <licenses>\n        <license>\n            <name>GNU Lesser General Public License v2.1</name>\n            <url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n    <scm>\n        <connection>scm:git:https://github.com/alibaba/COLA.git</connection>\n        <developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>\n        <url>https://github.com/alibaba/COLA</url>\n    </scm>\n    <issueManagement>\n        <url>https://github.com/alibaba/COLA/issues</url>\n        <system>GitHub Issues</system>\n    </issueManagement>\n    <developers>\n        <developer>\n            <id>significantfrank</id>\n            <name>Frank Zhang</name>\n            <email>25216348(at)qq.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>Architect</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/significantfrank</url>\n        </developer>\n        <developer>\n            <id>oldratlee</id>\n            <name>Jerry Lee</name>\n            <email>oldratlee(at)gmail.com</email>\n            <roles>\n                <role>Developer</role>\n                <role>CI/SCM Engineer</role>\n            </roles>\n            <timezone>+8</timezone>\n            <url>https://github.com/oldratlee</url>\n        </developer>\n    </developers>\n\n    <modules>\n        <module>cola-component-dto</module>\n        <module>cola-component-exception</module>\n        <module>cola-component-statemachine</module>\n        <module>cola-component-domain-starter</module>\n        <module>cola-component-extension-starter</module>\n        <module>cola-component-catchlog-starter</module>\n        <module>cola-component-test-container</module>\n        <module>cola-components-bom</module>\n        <module>cola-component-ruleengine</module>\n        <module>cola-component-unittest</module>\n    </modules>\n\n    <properties>\n        <java.version>17</java.version>\n        <maven.compiler.source>17</maven.compiler.source>\n        <maven.compiler.target>17</maven.compiler.target>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <spring.boot.version>3.3.0</spring.boot.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <!-- 测试包 -->\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-dependencies</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <!-- 工具包 -->\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>fastjson</artifactId>\n                <version>1.2.83</version>\n                <scope>provided</scope>\n            </dependency>\n            <dependency>\n                <groupId>commons-cli</groupId>\n                <artifactId>commons-cli</artifactId>\n                <version>1.8.0</version>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <!--\n                add maven-enforce-plugin to make sure the right jdk is used\n                https://stackoverflow.com/a/18420462/922688\n            -->\n            <plugin>\n                <artifactId>maven-enforcer-plugin</artifactId>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>enforce</goal>\n                        </goals>\n                        <configuration>\n                            <rules>\n                                <requireMavenVersion>\n                                    <version>3.3.9</version>\n                                </requireMavenVersion>\n                            </rules>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <artifactId>maven-resources-plugin</artifactId>\n                    <version>3.3.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-compiler-plugin</artifactId>\n                    <version>3.13.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-source-plugin</artifactId>\n                    <version>3.3.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-javadoc-plugin</artifactId>\n                    <version>3.7.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-gpg-plugin</artifactId>\n                    <version>3.1.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-enforcer-plugin</artifactId>\n                    <version>3.4.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-deploy-plugin</artifactId>\n                    <version>3.1.1</version>\n                </plugin>\n                <plugin>\n                    <groupId>org.sonatype.plugins</groupId>\n                    <artifactId>nexus-staging-maven-plugin</artifactId>\n                    <version>1.6.13</version>\n                </plugin>\n                <plugin>\n                    <groupId>org.jacoco</groupId>\n                    <artifactId>jacoco-maven-plugin</artifactId>\n                    <version>0.8.12</version>\n                </plugin>\n                <plugin>\n                    <groupId>pl.project13.maven</groupId>\n                    <artifactId>git-commit-id-plugin</artifactId>\n                    <version>4.9.10</version>\n                </plugin>\n                <!--修复老的maven-surefire-plugin不支持junit5 + spring5的bug-->\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-surefire-plugin</artifactId>\n                    <version>3.2.5</version>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n    </build>\n\n    <distributionManagement>\n        <snapshotRepository>\n            <id>ossrh</id>\n            <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n        </snapshotRepository>\n    </distributionManagement>\n\n    <profiles>\n        <profile>\n            <id>gen-java-src</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <artifactId>maven-source-plugin</artifactId>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>gen-java-doc</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <artifactId>maven-javadoc-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <id>attach-javadoc</id>\n                                <goals>\n                                    <goal>jar</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                        <configuration>\n                            <source>17</source>\n                            <show>protected</show>\n                            <charset>UTF-8</charset>\n                            <encoding>UTF-8</encoding>\n                            <docencoding>UTF-8</docencoding>\n                            <additionalJOptions>\n                                <additionalJOption>-quiet</additionalJOption>\n                                <additionalJOption>-J-Duser.language=en</additionalJOption>\n                                <additionalJOption>-J-Duser.country=US</additionalJOption>\n                                <additionalJOption>-Xdoclint:none</additionalJOption>\n                            </additionalJOptions>\n                        </configuration>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>gen-code-cov</id>\n            <activation>\n                <property>\n                    <name>env.TRAVIS</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <!--\n                            plugin docs: http://eclemma.org/jacoco/trunk/doc/\n                            for codecov.io, config example https://github.com/codecov/example-java\n                        -->\n                        <groupId>org.jacoco</groupId>\n                        <artifactId>jacoco-maven-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <goals>\n                                    <goal>prepare-agent</goal>\n                                </goals>\n                            </execution>\n                            <execution>\n                                <id>report</id>\n                                <phase>test</phase>\n                                <goals>\n                                    <goal>report</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>gen-sign</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <artifactId>maven-gpg-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <id>sign-artifacts</id>\n                                <phase>verify</phase>\n                                <goals>\n                                    <goal>sign</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>gen-git-properties</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <!--\n                                            Maven plugin which includes build-time git repository information into an POJO / *.properties).\n                                            Make your apps tell you which version exactly they were built from! Priceless in large distributed deployments.\n                                                https://github.com/ktoso/maven-git-commit-id-plugin\n                                        -->\n                    <plugin>\n                        <groupId>pl.project13.maven</groupId>\n                        <artifactId>git-commit-id-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <id>get-the-git-infos</id>\n                                <goals>\n                                    <goal>revision</goal>\n                                </goals>\n                            </execution>\n                            <execution>\n                                <id>validate-the-git-infos</id>\n                                <goals>\n                                    <goal>validateRevision</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                        <configuration>\n                            <validationProperties>\n                                <!-- verify that the current repository is not dirty -->\n                                <validationProperty>\n                                    <name>validating git dirty</name>\n                                    <value>${git.dirty}</value>\n                                    <shouldMatchTo>false</shouldMatchTo>\n                                </validationProperty>\n                            </validationProperties>\n                            <generateGitPropertiesFile>true</generateGitPropertiesFile>\n                            <generateGitPropertiesFilename>\n                                ${project.build.outputDirectory}/META-INF/scm/${project.groupId}/${project.artifactId}/git.properties\n                            </generateGitPropertiesFilename>\n                        </configuration>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>force-jdk11-when-release</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <!--\n                        add maven-enforce-plugin to make sure the right jdk is used\n                        https://stackoverflow.com/a/18420462/922688\n                    -->\n                    <plugin>\n                        <artifactId>maven-enforcer-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <goals>\n                                    <goal>enforce</goal>\n                                </goals>\n                                <configuration>\n                                    <rules>\n                                        <requireJavaVersion>\n                                            <version>17</version>\n                                        </requireJavaVersion>\n                                    </rules>\n                                </configuration>\n                            </execution>\n                        </executions>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>deploy-settings</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.sonatype.plugins</groupId>\n                        <artifactId>nexus-staging-maven-plugin</artifactId>\n                        <extensions>true</extensions>\n                        <configuration>\n                            <serverId>ossrh</serverId>\n                            <nexusUrl>https://oss.sonatype.org/</nexusUrl>\n                            <autoReleaseAfterClose>true</autoReleaseAfterClose>\n                        </configuration>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n    </profiles>\n</project>\n"
  },
  {
    "path": "cola-samples/charge/README.md",
    "content": "# 运营商计费系统\n计费系统是一个典型的复杂问题场景，比较适合采用COLA架构，且发挥Domain层的价值。故将其作为COLA的另一个Sample。\n\n运营商计费系统的需求如下：\n\n运营商向用户提供电话服务，支持用户拨打/接听电话，并对通话收取费用。\n如：主动拨打电话收取 0.5 元/分钟的通话费用；接听电话收取 0.4 元/分钟的通话费用。\n\n运营商为了吸引客户，定义了若干电话套餐，总共有三种类型的套餐。我们要设计一个**计费系统**用于套餐计费规则的执行，保存计费记录，并通知**账户系统**扣减费用。\n\n_注意：在一次通话过程中，通话控制系统可能会调用多次计费系统进行计费。_\n\n- 基础套餐\n    1. 主叫收费 0.5 元/分钟\n    2. 被叫收费 0.4 元/分钟\n\n\n- 固定时长套餐\n    1. 套餐月固定费 100 元，包含：200 分钟主叫通话时间+200 分钟被叫接听时间\n    2. 套餐外部分不再参与打折优惠，主叫 0.5 元/分钟，被叫 0.4 元/分钟\n\n\n- 家庭套餐\n    1. 套餐月固定费 20 元\n    2. 用户可以指定 N 个号码作为自己的亲情号\n    3. 用户接听/拨打亲情号均不收费\n    4. 与亲情号之外的号码通话，主叫 0.5 元/分钟，被叫 0.4 元/分钟\n\n# 系统设计\n## 统一语言\n我经常说，建模就是在分析语言，对于任何问题域，熟悉领域知识、理清概念、统一语言都是非常必要且重要的事情。\n对于这个系统也不例外。\n![img_1.png](img_1.png)\n\n## 计费系统和周边系统的关系\n![img.png](img.png)\n\n\n\n"
  },
  {
    "path": "cola-samples/charge/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         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.huawei</groupId>\n    <artifactId>charging-system</artifactId>\n    <version>1.0.0-SNAPSHOT</version>\n\n    <properties>\n        <java.version>17</java.version>\n        <maven.compiler.source>17</maven.compiler.source>\n        <maven.compiler.target>17</maven.compiler.target>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\n        <springfox.version>3.0.0</springfox.version>\n        <archunit.version>1.3.0</archunit.version>\n        <spring.boot.version>3.2.0</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-dependencies</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.22</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-jpa</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>8.0.33</version>\n        </dependency>\n        <!-- Unit Test Support start-->\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-test-container</artifactId>\n            <version>4.4.0-SNAPSHOT</version>\n        </dependency>\n        <!--this for embedded database unit test-->\n        <dependency>\n            <groupId>com.h2database</groupId>\n            <artifactId>h2</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <!--this for dependent service mock, to avoid conflict, we'd better use standalone -->\n        <dependency>\n            <groupId>org.wiremock</groupId>\n            <artifactId>wiremock-standalone</artifactId>\n            <version>3.5.4</version>\n            <scope>test</scope>\n        </dependency>\n        <!-- Unit Test Support End-->\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/Application.java",
    "content": "package com.huawei.charging;\r\n\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\n@SpringBootApplication\r\npublic class Application {\r\n\r\n    public static void main(String[] args) {\r\n        SpringApplication.run(Application.class, args);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/adapter/ChargeController.java",
    "content": "package com.huawei.charging.adapter;\n\nimport com.huawei.charging.application.ChargeServiceI;\nimport com.huawei.charging.application.dto.*;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.web.bind.annotation.*;\n\n\n@RestController\n@Slf4j\npublic class ChargeController {\n\n    @Resource\n    private ChargeServiceI chargeService;\n\n    @PostMapping(\"session/{sessionId}/begin\")\n    public Response begin(@PathVariable(name = \"sessionId\") String sessionId,\n                          @RequestParam(\"callingPhoneNo\") String callingPhoneNo,\n                          @RequestParam(\"calledPhoneNo\") String calledPhoneNo) {\n        log.debug(sessionId + \" \" + callingPhoneNo + \" \" + calledPhoneNo);\n        BeginSessionRequest request = new BeginSessionRequest(sessionId, Long.valueOf(callingPhoneNo), Long.valueOf(calledPhoneNo));\n        return chargeService.begin(request);\n    }\n\n    @PostMapping(\"session/{sessionId}/charge\")\n    public Response charge(@PathVariable(name = \"sessionId\") String sessionId,\n                       @RequestParam int duration) {\n        log.debug(sessionId + \" \" + duration);\n        ChargeRequest request = new ChargeRequest(sessionId, duration);\n        return chargeService.charge(request);\n    }\n\n    @PostMapping(\"session/{sessionId}/end\")\n    public Response end(@PathVariable(name = \"sessionId\") String sessionId,\n                    @RequestParam int duration) {\n        log.debug(sessionId + \" \" + duration);\n        EndSessionRequest request = new EndSessionRequest(sessionId, duration);\n        return chargeService.end(request);\n    }\n\n    @GetMapping(\"{sessionId}/chargeRecords\")\n    public MultiResponse<ChargeRecordDto> getChargeRecord(@PathVariable(name = \"sessionId\") String sessionId) {\n        return chargeService.listChargeRecords(sessionId);\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/application/ChargeServiceI.java",
    "content": "package com.huawei.charging.application;\r\n\r\nimport com.huawei.charging.application.dto.*;\r\n\r\npublic interface ChargeServiceI {\r\n    Response begin(BeginSessionRequest request);\r\n\r\n    Response charge(ChargeRequest request);\r\n\r\n    Response end(EndSessionRequest request);\r\n\r\n    MultiResponse<ChargeRecordDto> listChargeRecords(String sessionId);\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/application/ChargeServiceImpl.java",
    "content": "package com.huawei.charging.application;\n\nimport com.huawei.charging.application.dto.*;\nimport com.huawei.charging.domain.account.Account;\nimport com.huawei.charging.domain.account.AccountDomainService;\nimport com.huawei.charging.domain.charge.CallType;\nimport com.huawei.charging.domain.charge.ChargeContext;\nimport com.huawei.charging.domain.charge.ChargeRecord;\nimport com.huawei.charging.domain.charge.Session;\nimport com.huawei.charging.domain.gateway.AccountGateway;\nimport com.huawei.charging.domain.gateway.ChargeGateway;\nimport com.huawei.charging.domain.gateway.SessionGateway;\nimport jakarta.annotation.Resource;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.stereotype.Service;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Service\n@Slf4j\npublic class ChargeServiceImpl implements ChargeServiceI {\n\n    @Resource\n    private SessionGateway sessionGateway;\n\n    @Resource\n    private AccountGateway accountGateway;\n\n    @Resource\n    private AccountDomainService accountDomainService;\n\n    @Resource\n    private ChargeGateway chargeGateway;\n\n    @Override\n    public Response begin(BeginSessionRequest request) {\n        Session session = request.toSession();\n        accountDomainService.canSessionStart(session);\n        sessionGateway.create(session);\n        log.debug(\"Session created successfully :\" + session);\n        return Response.buildSuccess();\n    }\n\n    @Override\n    public Response charge(ChargeRequest request) {\n        log.debug(\"Do charge : \" + request);\n        Session session = sessionGateway.get(request.getSessionId());\n        int durationToCharge = request.getDuration() - session.getChargedDuration();\n        List<ChargeRecord> chargeRecordList = new ArrayList<>();\n        chargeCalling(session, durationToCharge, chargeRecordList);\n        chargeCalled(session, durationToCharge, chargeRecordList);\n        chargeGateway.saveAll(chargeRecordList);\n        session.setChargedDuration(request.getDuration());\n        return Response.buildSuccess();\n    }\n\n    private void chargeCalling(Session session, int durationToCharge, List<ChargeRecord> chargeRecordList) {\n        Account callingAccount = accountGateway.getAccount(session.getCallingPhoneNo());\n        ChargeContext callingCtx = new ChargeContext(CallType.CALLING, session.getCallingPhoneNo(), session.getCalledPhoneNo(), durationToCharge);\n        callingCtx.session = session;\n        callingCtx.account = callingAccount;\n        chargeRecordList.addAll(callingAccount.charge(callingCtx));\n    }\n\n    private void chargeCalled(Session session, int durationToCharge, List<ChargeRecord> chargeRecordList) {\n        Account calledAccount = accountGateway.getAccount(session.getCalledPhoneNo());\n        ChargeContext calledCtx = new ChargeContext(CallType.CALLED, session.getCalledPhoneNo(), session.getCallingPhoneNo(), durationToCharge);\n        calledCtx.session = session;\n        calledCtx.account = calledAccount;\n        chargeRecordList.addAll(calledAccount.charge(calledCtx));\n    }\n\n    @Override\n    public Response end(EndSessionRequest request) {\n        charge(request.toChargeRequest());\n        sessionGateway.end(request.getSessionId());\n        return Response.buildSuccess();\n    }\n\n    @Override\n    public MultiResponse<ChargeRecordDto> listChargeRecords(String sessionId) {\n        List<ChargeRecord> chargeRecordList = chargeGateway.findBySessionId(sessionId);\n        List<ChargeRecordDto> chargeRecordDtoList = new ArrayList<>();\n        for (ChargeRecord chargeRecord : chargeRecordList) {\n            chargeRecordDtoList.add(ChargeRecordDto.fromEntity(chargeRecord));\n        }\n        return MultiResponse.of(chargeRecordDtoList);\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/application/dto/BeginSessionRequest.java",
    "content": "package com.huawei.charging.application.dto;\r\n\r\nimport com.huawei.charging.domain.charge.Session;\r\nimport lombok.Data;\r\n\r\n@Data\r\npublic class BeginSessionRequest {\r\n\r\n    /**\r\n     * 本次通话的UUID\r\n     */\r\n    private String sessionId;\r\n\r\n    /**\r\n     * 主叫电话号码\r\n     */\r\n    private long callingPhoneNo;\r\n\r\n    /**\r\n     * 被叫电话号码\r\n     */\r\n    private long calledPhoneNo;\r\n\r\n    public Session toSession(){\r\n        return new Session(sessionId, callingPhoneNo, calledPhoneNo);\r\n    }\r\n\r\n    public BeginSessionRequest() {\r\n    }\r\n\r\n    public BeginSessionRequest(String sessionId, long callingPhoneNo, long calledPhoneNo) {\r\n        this.sessionId = sessionId;\r\n        this.callingPhoneNo = callingPhoneNo;\r\n        this.calledPhoneNo = calledPhoneNo;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/application/dto/ChargeRecordDto.java",
    "content": "package com.huawei.charging.application.dto;\r\n\r\nimport com.huawei.charging.domain.charge.CallType;\r\nimport com.huawei.charging.domain.charge.ChargeRecord;\r\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlanType;\r\n\r\npublic class ChargeRecordDto {\r\n    public Long id;\r\n    public String sessionId;\r\n    public long phoneNo;\r\n    public int chargeDuration;\r\n    public long cost;\r\n    public CallType callType;\r\n    public ChargePlanType chargePlanType;\r\n\r\n    public static ChargeRecordDto fromEntity(ChargeRecord chargeRecord){\r\n        ChargeRecordDto dto = new ChargeRecordDto();\r\n        dto.id = chargeRecord.getId();\r\n        dto.sessionId = chargeRecord.getSessionId();\r\n        dto.phoneNo = chargeRecord.getPhoneNo();\r\n        dto.chargeDuration = chargeRecord.getChargeDuration();\r\n        dto.cost = chargeRecord.getCost().getAmount();\r\n        dto.callType = chargeRecord.getCallType();\r\n        dto.chargePlanType = chargeRecord.getChargePlanType();\r\n        return dto;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/application/dto/ChargeRequest.java",
    "content": "package com.huawei.charging.application.dto;\r\n\r\nimport lombok.Data;\r\n\r\n@Data\r\npublic class ChargeRequest {\r\n\r\n    private String sessionId;\r\n\r\n    /**\r\n     * 当前通话，截止目前的累计时间\r\n     */\r\n    private int duration;\r\n\r\n    public ChargeRequest() {\r\n    }\r\n\r\n    public ChargeRequest(String sessionId, int duration) {\r\n        this.sessionId = sessionId;\r\n        this.duration = duration;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/application/dto/EndSessionRequest.java",
    "content": "package com.huawei.charging.application.dto;\r\n\r\nimport lombok.Data;\r\n\r\n@Data\r\npublic class EndSessionRequest {\r\n    private String sessionId;\r\n\r\n    /**\r\n     * 当前通话，截止目前的累计时间\r\n     */\r\n    private int duration;\r\n\r\n    public ChargeRequest toChargeRequest() {\r\n        ChargeRequest chargeRequest = new ChargeRequest();\r\n        chargeRequest.setSessionId(sessionId);\r\n        chargeRequest.setDuration(duration);\r\n        return chargeRequest;\r\n    }\r\n\r\n    public EndSessionRequest() {\r\n    }\r\n\r\n    public EndSessionRequest(String sessionId, int duration) {\r\n        this.sessionId = sessionId;\r\n        this.duration = duration;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/application/dto/MultiResponse.java",
    "content": "package com.huawei.charging.application.dto;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.Collections;\r\nimport java.util.List;\r\n\r\npublic class MultiResponse<T> extends Response {\r\n\r\n    private static final long serialVersionUID = 1L;\r\n\r\n    private Collection<T> data;\r\n\r\n    public List<T> getData() {\r\n        if (null == data) {\r\n            return Collections.emptyList();\r\n        }\r\n        if (data instanceof List) {\r\n            return (List<T>) data;\r\n        }\r\n        return new ArrayList<>(data);\r\n    }\r\n\r\n    public void setData(Collection<T> data) {\r\n        this.data = data;\r\n    }\r\n\r\n    public boolean isEmpty() {\r\n        return data == null || data.isEmpty();\r\n    }\r\n\r\n    public boolean isNotEmpty() {\r\n        return !isEmpty();\r\n    }\r\n\r\n    public static MultiResponse buildSuccess() {\r\n        MultiResponse response = new MultiResponse();\r\n        response.setSuccess(true);\r\n        return response;\r\n    }\r\n\r\n    public static MultiResponse buildFailure(String errCode, String errMessage) {\r\n        MultiResponse response = new MultiResponse();\r\n        response.setSuccess(false);\r\n        response.setErrCode(errCode);\r\n        response.setErrMessage(errMessage);\r\n        return response;\r\n    }\r\n\r\n    public static <T> MultiResponse<T> of(Collection<T> data) {\r\n        MultiResponse<T> response = new MultiResponse<>();\r\n        response.setSuccess(true);\r\n        response.setData(data);\r\n        return response;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/application/dto/Response.java",
    "content": "package com.huawei.charging.application.dto;\r\n\r\npublic class Response {\r\n    private boolean success;\r\n\r\n    private String errCode;\r\n\r\n    private String errMessage;\r\n\r\n    public boolean isSuccess() {\r\n        return success;\r\n    }\r\n\r\n    public void setSuccess(boolean success) {\r\n        this.success = success;\r\n    }\r\n\r\n    public String getErrCode() {\r\n        return errCode;\r\n    }\r\n\r\n    public void setErrCode(String errCode) {\r\n        this.errCode = errCode;\r\n    }\r\n\r\n    public String getErrMessage() {\r\n        return errMessage;\r\n    }\r\n\r\n    public void setErrMessage(String errMessage) {\r\n        this.errMessage = errMessage;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return \"Response [success=\" + success + \", errCode=\" + errCode + \", errMessage=\" + errMessage + \"]\";\r\n    }\r\n\r\n    public static Response buildSuccess() {\r\n        Response response = new Response();\r\n        response.setSuccess(true);\r\n        return response;\r\n    }\r\n\r\n    public static Response buildFailure(String errCode, String errMessage) {\r\n        Response response = new Response();\r\n        response.setSuccess(false);\r\n        response.setErrCode(errCode);\r\n        response.setErrMessage(errMessage);\r\n        return response;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/application/dto/SingleResponse.java",
    "content": "package com.huawei.charging.application.dto;\r\n\r\npublic class SingleResponse<T> extends Response {\r\n\r\n    private static final long serialVersionUID = 1L;\r\n\r\n    private T data;\r\n\r\n    public T getData() {\r\n        return data;\r\n    }\r\n\r\n    public void setData(T data) {\r\n        this.data = data;\r\n    }\r\n\r\n    public static SingleResponse buildSuccess() {\r\n        SingleResponse response = new SingleResponse();\r\n        response.setSuccess(true);\r\n        return response;\r\n    }\r\n\r\n    public static SingleResponse buildFailure(String errCode, String errMessage) {\r\n        SingleResponse response = new SingleResponse();\r\n        response.setSuccess(false);\r\n        response.setErrCode(errCode);\r\n        response.setErrMessage(errMessage);\r\n        return response;\r\n    }\r\n\r\n    public static <T> SingleResponse<T> of(T data) {\r\n        SingleResponse<T> response = new SingleResponse<>();\r\n        response.setSuccess(true);\r\n        response.setData(data);\r\n        return response;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/ApplicationContextHelper.java",
    "content": "package com.huawei.charging.domain;\r\n\r\nimport org.springframework.beans.BeansException;\r\nimport org.springframework.context.ApplicationContext;\r\nimport org.springframework.context.ApplicationContextAware;\r\nimport org.springframework.stereotype.Component;\r\n\r\n@Component\r\npublic class ApplicationContextHelper implements ApplicationContextAware {\r\n    private static ApplicationContext applicationContext;\r\n\r\n    @Override\r\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\r\n        ApplicationContextHelper.applicationContext = applicationContext;\r\n    }\r\n\r\n    public static <T> T getBean(Class<T> targetClz) {\r\n        T beanInstance = null;\r\n        //优先按type查\r\n        try {\r\n            beanInstance = (T)applicationContext.getBean(targetClz);\r\n        } catch (Exception e) {\r\n        }\r\n        //按name查\r\n        if (beanInstance == null) {\r\n            String simpleName = targetClz.getSimpleName();\r\n            //首字母小写\r\n            simpleName = Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);\r\n            beanInstance = (T)applicationContext.getBean(simpleName);\r\n        }\r\n        if (beanInstance == null) {\r\n            throw new RuntimeException(\"Component \" + targetClz + \" can not be found in Spring Container\");\r\n        }\r\n        return beanInstance;\r\n    }\r\n\r\n    public static Object getBean(String claz) {\r\n        return ApplicationContextHelper.applicationContext.getBean(claz);\r\n    }\r\n\r\n    public static <T> T getBean(String name, Class<T> requiredType) {\r\n        return ApplicationContextHelper.applicationContext.getBean(name, requiredType);\r\n    }\r\n\r\n    public static <T> T getBean(Class<T> requiredType, Object... params) {\r\n        return ApplicationContextHelper.applicationContext.getBean(requiredType, params);\r\n    }\r\n\r\n    public static ApplicationContext getApplicationContext() {\r\n        return applicationContext;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/BizException.java",
    "content": "package com.huawei.charging.domain;\r\n\r\npublic class BizException extends RuntimeException{\r\n\r\n    public BizException(String errMessage) {\r\n        super(errMessage);\r\n    }\r\n\r\n    public static BizException of(String errMessage){\r\n        return new BizException(errMessage);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/DomainFactory.java",
    "content": "package com.huawei.charging.domain;\r\n\r\npublic class DomainFactory {\r\n\r\n    public static <T> T get(Class<T> entityClz){\r\n        return ApplicationContextHelper.getBean(entityClz);\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/Entity.java",
    "content": "package com.huawei.charging.domain;\r\n\r\nimport org.springframework.beans.factory.config.ConfigurableBeanFactory;\r\nimport org.springframework.context.annotation.Scope;\r\nimport org.springframework.stereotype.Component;\r\n\r\nimport java.lang.annotation.*;\r\n\r\n@Inherited\r\n@Retention(RetentionPolicy.RUNTIME)\r\n@Target({ElementType.TYPE})\r\n@Component\r\n@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)\r\npublic @interface Entity {\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/account/Account.java",
    "content": "package com.huawei.charging.domain.account;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.huawei.charging.domain.BizException;\nimport com.huawei.charging.domain.DomainFactory;\nimport com.huawei.charging.domain.Entity;\nimport com.huawei.charging.domain.charge.*;\nimport com.huawei.charging.domain.charge.chargeplan.BasicChargePlan;\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlan;\nimport com.huawei.charging.domain.charge.chargerule.ChargeRuleFactory;\nimport com.huawei.charging.domain.charge.chargerule.CompositeChargeRule;\nimport com.huawei.charging.domain.gateway.AccountGateway;\nimport jakarta.annotation.Resource;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\n\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\n@Data\n@Entity\n@Slf4j\npublic class Account {\n    /**\n     * 用户号码\n     */\n    private long phoneNo;\n\n    /**\n     * 账户余额\n     */\n    private Money remaining;\n\n    /**\n     * 账户所拥有的套餐\n     */\n    @JsonIgnore\n    private List<ChargePlan> chargePlanList = new ArrayList<>();;\n\n    @Resource\n    private AccountGateway accountGateway;\n\n    private String name;\n\n    public Account(){\n\n    }\n\n    public Account(long phoneNo, Money amount, List<ChargePlan> chargePlanList){\n        this.phoneNo = phoneNo;\n        this.remaining = amount;\n        this.chargePlanList = chargePlanList;\n    }\n\n    public static Account valueOf(long phoneNo, Money amount) {\n        Account account = DomainFactory.get(Account.class);\n        account.setPhoneNo(phoneNo);\n        account.setRemaining(amount);\n        account.chargePlanList.add(new BasicChargePlan());\n        return account;\n    }\n\n    /**\n     * 检查账户余额是否足够\n     */\n    public void checkRemaining() {\n        if (remaining.isLessThan(Money.of(0))) {\n            throw BizException.of(this.phoneNo + \" has insufficient amount\");\n        }\n    }\n\n    public List<ChargeRecord> charge(ChargeContext ctx) {\n        CompositeChargeRule compositeChargeRule = ChargeRuleFactory.get(chargePlanList);\n        List<ChargeRecord> chargeRecords = compositeChargeRule.doCharge(ctx);\n        log.debug(\"Charges: \"+ chargeRecords);\n\n        //跟新账户系统\n        accountGateway.sync(phoneNo, chargeRecords);\n        return chargeRecords;\n    }\n\n    @Override\n    public String toString() {\n        return \"Account{\" +\n                \"phoneNo=\" + phoneNo +\n                \", remaining=\" + remaining +\n                \", chargePlanList=\" + chargePlanList +\n                \", name=\" + name +\n                '}';\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/account/AccountDomainService.java",
    "content": "package com.huawei.charging.domain.account;\n\nimport com.huawei.charging.domain.charge.Session;\nimport com.huawei.charging.domain.gateway.AccountGateway;\nimport jakarta.annotation.Resource;\nimport org.springframework.stereotype.Component;\n\n\n@Component\npublic class AccountDomainService {\n\n    @Resource\n    private AccountGateway accountGateway;\n\n    public void canSessionStart(Session session){\n        Account callingAccount = accountGateway.getAccount(session.getCallingPhoneNo());\n        Account calledAccount = accountGateway.getAccount(session.getCalledPhoneNo());\n        callingAccount.checkRemaining();\n        calledAccount.checkRemaining();\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/CallType.java",
    "content": "package com.huawei.charging.domain.charge;\r\n\r\npublic enum CallType {\r\n    /**\r\n     * 主叫\r\n     */\r\n    CALLING,\r\n    /**\r\n     * 被叫\r\n     */\r\n    CALLED\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/ChargeContext.java",
    "content": "package com.huawei.charging.domain.charge;\r\n\r\nimport com.huawei.charging.domain.account.Account;\r\nimport lombok.Data;\r\n\r\n@Data\r\npublic class ChargeContext {\r\n\r\n    /**\r\n     * 本次通话的Session\r\n     */\r\n    public Session session;\r\n\r\n    /**\r\n     * 呼叫类型\r\n     */\r\n    public CallType callType;\r\n    /**\r\n     * 账号号码\r\n     */\r\n    public long phoneNo;\r\n\r\n    /**\r\n     * 通话另一端号码\r\n     */\r\n    public long otherSidePhoneNo;\r\n\r\n    /**\r\n     * 当前需要被扣费的时长\r\n     */\r\n    public int durationToCharge;\r\n\r\n    /**\r\n     * 被Charge的账号\r\n     */\r\n    public Account account;\r\n\r\n    public ChargeContext(CallType callType, long phoneNo, long otherSidePhoneNo, int durationToCharge) {\r\n        this.callType = callType;\r\n        this.phoneNo = phoneNo;\r\n        this.otherSidePhoneNo = otherSidePhoneNo;\r\n        this.durationToCharge = durationToCharge;\r\n    }\r\n\r\n    public boolean needCharge(){\r\n        return durationToCharge >0;\r\n    }\r\n\r\n    public boolean isCalling(){\r\n        return CallType.CALLING ==  this.callType;\r\n    }\r\n\r\n    public boolean isCalled(){\r\n        return CallType.CALLED == this.callType;\r\n    }\r\n\r\n    @Override\r\n    public String toString() {\r\n        return \"ChargeContext{\" +\r\n                \"callType=\" + callType +\r\n                \", phoneNo=\" + phoneNo +\r\n                \", otherSidePhoneNo=\" + otherSidePhoneNo +\r\n                \", durationToCharge=\" + durationToCharge +\r\n                \", account=\" + account +\r\n                '}';\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/ChargeRecord.java",
    "content": "package com.huawei.charging.domain.charge;\n\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlanType;\nimport lombok.Data;\n\nimport jakarta.persistence.*;\nimport java.util.Date;\n\n@Entity\n@Table(name = \"charge_record\")\n@Data\npublic class ChargeRecord {\n\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Long Id;\n\n    private String sessionId;\n\n    private long phoneNo;\n\n    /**\n     * 呼叫类型\n     */\n    @Enumerated(EnumType.STRING)\n    private CallType callType;\n\n    /**\n     * 计费记录所对应的呼叫时长\n     */\n    private int chargeDuration;\n\n    /**\n     * 所属计费套餐\n     */\n    @Enumerated(EnumType.STRING)\n    private ChargePlanType chargePlanType;\n\n    private Money cost;\n\n    @Temporal(TemporalType.TIMESTAMP)\n    public Date createTime;\n\n    @Temporal(TemporalType.TIMESTAMP)\n    public Date updateTime;\n\n    public ChargeRecord() {\n    }\n\n    public ChargeRecord(long phoneNo, CallType callType, int chargeDuration, ChargePlanType chargePlanType, Money cost) {\n        this.phoneNo = phoneNo;\n        this.callType = callType;\n        this.chargeDuration = chargeDuration;\n        this.chargePlanType = chargePlanType;\n        this.cost = cost;\n    }\n\n    @Override\n    public String toString() {\n        return \"Charge{\" +\n                \"phoneNo=\" + phoneNo +\n                \", callType=\" + callType +\n                \", chargeDuration=\" + chargeDuration +\n                \", chargePlanType=\" + chargePlanType +\n                \", cost=\" + cost +\n                '}';\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/Money.java",
    "content": "package com.huawei.charging.domain.charge;\r\n\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.Objects;\r\n\r\n/**\r\n * 这个Money是简化版的，真实场景应该用BigDecimal\r\n */\r\n@Data\r\npublic class Money {\r\n\r\n    /**\r\n     * 单位是角，1代表0.1元， 10代表1元\r\n     */\r\n    private int amount;\r\n\r\n    public Money(int amount) {\r\n        this.amount = amount;\r\n    }\r\n\r\n    public static Money of(int amount){\r\n        return new Money(amount);\r\n    }\r\n\r\n    public boolean isLessThan(Money money){\r\n        return this.amount <= money.getAmount();\r\n    }\r\n\r\n    public void minus(Money money){\r\n        this.amount =  this.amount - money.getAmount();\r\n    }\r\n\r\n    @Override\r\n    public boolean equals(Object o) {\r\n        if (this == o) return true;\r\n        if (o == null || getClass() != o.getClass()) return false;\r\n        Money money = (Money) o;\r\n        return amount == money.amount;\r\n    }\r\n\r\n    @Override\r\n    public int hashCode() {\r\n        return Objects.hash(amount);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/MoneyConverter.java",
    "content": "package com.huawei.charging.domain.charge;\n\n\nimport jakarta.persistence.AttributeConverter;\nimport jakarta.persistence.Converter;\n\n@Converter(autoApply = true)\npublic class MoneyConverter implements AttributeConverter<Money,Long> {\n    @Override\n    public Long convertToDatabaseColumn(Money entityData) {\n        return Long.valueOf(entityData.getAmount());\n    }\n\n    @Override\n    public Money convertToEntityAttribute(Long dbData) {\n        return Money.of(dbData.intValue());\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/Session.java",
    "content": "package com.huawei.charging.domain.charge;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\n@Data\r\npublic class Session {\r\n    private String sessionId;\r\n\r\n    /**\r\n     * 主叫电话号码\r\n     */\r\n    private long callingPhoneNo;\r\n\r\n    /**\r\n     * 被叫电话号码\r\n     */\r\n    private long calledPhoneNo;\r\n\r\n    /**\r\n     * 当前通话已扣费的时长\r\n     *\r\n     */\r\n    private int chargedDuration;\r\n\r\n    /**\r\n     * 当前通话产生的Charge记录\r\n     */\r\n    private List<ChargeRecord> chargeRecordList = new ArrayList<>();\r\n\r\n    public Session(String sessionId, long callingPhoneNo, long calledPhoneNo) {\r\n        this.sessionId = sessionId;\r\n        this.callingPhoneNo = callingPhoneNo;\r\n        this.calledPhoneNo = calledPhoneNo;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargeplan/BasicChargePlan.java",
    "content": "package com.huawei.charging.domain.charge.chargeplan;\r\n\r\npublic class BasicChargePlan extends ChargePlan<BasicChargePlan.BasicChargeFee>{\r\n\r\n    public BasicChargePlan(){\r\n        this.priority = 0;\r\n    }\r\n\r\n    @Override\r\n    public BasicChargeFee getResource() {\r\n        return new BasicChargeFee();\r\n    }\r\n\r\n    @Override\r\n    public ChargePlanType getType() {\r\n        return ChargePlanType.BASIC;\r\n    }\r\n\r\n    public static class BasicChargeFee implements Resource{\r\n        /**\r\n         * 主叫单价。单位是角，5表示0.5元每分钟\r\n         */\r\n        public final int CALLING_PRICE = 5;\r\n\r\n        /**\r\n         * 主叫单价。单位是角，4表示0.4元每分钟\r\n         */\r\n        public final int CALLED_PRICE = 4;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargeplan/ChargePlan.java",
    "content": "package com.huawei.charging.domain.charge.chargeplan;\n\npublic abstract class ChargePlan<T extends Resource> implements Comparable<ChargePlan>{\n\n    protected int priority;\n\n    public abstract  T getResource();\n\n    public abstract ChargePlanType getType();\n\n    public ChargePlan(){\n\n    }\n    /**\n     * 不同套餐之间的优先级关系\n     * @param other the object to be compared.\n     * @return\n     */\n    @Override\n    public int compareTo(ChargePlan other) {\n        return other.priority - this.priority;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChargePlan{chargeType=\" + getType()+\n                \", priority=\" + priority +\n                '}';\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargeplan/ChargePlanType.java",
    "content": "package com.huawei.charging.domain.charge.chargeplan;\r\n\r\npublic enum ChargePlanType {\r\n    /**\r\n     * 基础套餐\r\n     */\r\n    BASIC,\r\n    /**\r\n     * 固定时常套餐\r\n     */\r\n    FIXED_TIME,\r\n    /**\r\n     * 家庭套餐\r\n     */\r\n    FAMILY\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargeplan/FamilyChargePlan.java",
    "content": "package com.huawei.charging.domain.charge.chargeplan;\r\n\r\nimport java.util.HashSet;\r\nimport java.util.Set;\r\n\r\npublic class FamilyChargePlan extends ChargePlan<FamilyChargePlan.FamilyMember> {\r\n\r\n    public FamilyChargePlan() {\r\n        this.priority = 2;\r\n    }\r\n\r\n    @Override\r\n    public FamilyMember getResource() {\r\n        return new FamilyMember();\r\n    }\r\n\r\n    @Override\r\n    public ChargePlanType getType() {\r\n        return ChargePlanType.FAMILY;\r\n    }\r\n\r\n    public static class FamilyMember implements Resource{\r\n        private Set<Long> familyMembers = new HashSet<>();\r\n\r\n        /**\r\n         * Mock here, 真实场景，情亲号码肯定也是从外系统获取的\r\n         */\r\n        public FamilyMember() {\r\n            familyMembers.add(13681874561L);\r\n            familyMembers.add(15921582125L);\r\n        }\r\n\r\n        public boolean isMember(long phoneNo) {\r\n            return familyMembers.contains(phoneNo);\r\n        }\r\n    }\r\n}\r\n\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargeplan/FixedTimeChangePlan.java",
    "content": "package com.huawei.charging.domain.charge.chargeplan;\r\n\r\npublic class FixedTimeChangePlan extends ChargePlan<FixedTimeChangePlan.FreeCallTime>{\r\n\r\n    public FixedTimeChangePlan() {\r\n        this.priority=1;\r\n    }\r\n\r\n    @Override\r\n    public FreeCallTime getResource() {\r\n        return new FreeCallTime();\r\n    }\r\n\r\n    @Override\r\n    public ChargePlanType getType() {\r\n        return ChargePlanType.FIXED_TIME;\r\n    }\r\n\r\n    public static class FreeCallTime implements Resource{\r\n        public static int FREE_CALLING_TIME = 200;\r\n        public static int FREE_CALLED_TIME = 200;\r\n\r\n        public boolean isCallingTimeRemaining(){\r\n            return FREE_CALLING_TIME > 0;\r\n        }\r\n\r\n        /**\r\n         * 扣减固定时长套餐的费用\r\n         * @param duration 扣减时长\r\n         * @return 剩余还需要扣减的时长\r\n         */\r\n        public int chargeFreeCallingTime(int duration){\r\n            if(duration > FREE_CALLING_TIME){\r\n                int durationToCharge = duration - FREE_CALLING_TIME;\r\n                FREE_CALLING_TIME = 0;\r\n                return durationToCharge;\r\n            }\r\n            else{\r\n                FREE_CALLING_TIME = FREE_CALLING_TIME - duration;\r\n                return 0;\r\n            }\r\n        }\r\n\r\n        public boolean isCalledTimeRemaining(){\r\n            return FREE_CALLED_TIME > 0;\r\n        }\r\n\r\n        /**\r\n         * 扣减固定时长套餐的费用\r\n         * @param duration 扣减时长\r\n         * @return 剩余还需要扣减的时长\r\n         */\r\n        public int chargeFreeCalledTime(int duration){\r\n            if(duration > FREE_CALLED_TIME){\r\n                int durationToCharge = duration - FREE_CALLED_TIME;\r\n                FREE_CALLED_TIME = 0;\r\n                return durationToCharge;\r\n            }\r\n            else{\r\n                FREE_CALLED_TIME = FREE_CALLED_TIME - duration;\r\n                return 0;\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargeplan/Resource.java",
    "content": "package com.huawei.charging.domain.charge.chargeplan;\r\n\r\n/**\r\n * 套餐背后所绑定的资源\r\n */\r\npublic interface Resource {\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargerule/AbstractChargeRule.java",
    "content": "package com.huawei.charging.domain.charge.chargerule;\r\n\r\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlan;\r\n\r\npublic abstract class AbstractChargeRule implements ChargeRule{\r\n    protected ChargePlan chargePlan;\r\n\r\n    @Override\r\n    public void belongsTo(ChargePlan chargePlan){\r\n        this.chargePlan = chargePlan;\r\n    }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargerule/BasicChargeRule.java",
    "content": "package com.huawei.charging.domain.charge.chargerule;\r\n\r\nimport com.huawei.charging.domain.charge.*;\r\nimport com.huawei.charging.domain.charge.chargeplan.BasicChargePlan;\r\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlanType;\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport org.springframework.stereotype.Component;\r\n\r\n@Component\r\n@Slf4j\r\npublic class BasicChargeRule extends AbstractChargeRule{\r\n    @Override\r\n    public ChargeRecord doCharge(ChargeContext ctx) {\r\n        if(!ctx.needCharge()){\r\n            log.debug(\"No need charge for : \"+ctx);\r\n            return null;\r\n        }\r\n        BasicChargePlan basicChargePlan = (BasicChargePlan)chargePlan;\r\n        BasicChargePlan.BasicChargeFee chargeFee = basicChargePlan.getResource();\r\n        Money cost;\r\n        int duration = ctx.durationToCharge;\r\n        if (ctx.callType == CallType.CALLING) {\r\n            cost = Money.of(duration * chargeFee.CALLING_PRICE);\r\n        } else {\r\n            cost = Money.of(duration * chargeFee.CALLED_PRICE);\r\n        }\r\n        ChargeRecord chargeRecord = new ChargeRecord(ctx.phoneNo, ctx.callType, duration, ChargePlanType.BASIC, cost);\r\n\r\n        //在账号上扣减费用\r\n        ctx.account.getRemaining().minus(cost);\r\n        ctx.setDurationToCharge(0);\r\n        return chargeRecord;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargerule/ChargeRule.java",
    "content": "package com.huawei.charging.domain.charge.chargerule;\r\n\r\nimport com.huawei.charging.domain.charge.ChargeRecord;\r\nimport com.huawei.charging.domain.charge.ChargeContext;\r\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlan;\r\n\r\npublic interface ChargeRule {\r\n    ChargeRecord doCharge(ChargeContext ctx);\r\n\r\n    void belongsTo(ChargePlan chargePlan);\r\n\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargerule/ChargeRuleFactory.java",
    "content": "package com.huawei.charging.domain.charge.chargerule;\r\n\r\nimport com.huawei.charging.domain.ApplicationContextHelper;\r\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlan;\r\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlanType;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.Collections;\r\nimport java.util.List;\r\n\r\npublic class ChargeRuleFactory {\r\n    public static CompositeChargeRule get(List<ChargePlan> chargePlanList) {\r\n        //按套餐的优先级进行排序\r\n        Collections.sort(chargePlanList);\r\n\r\n        List<ChargeRule> chargeRules = new ArrayList<>();\r\n        for (ChargePlan chargePlan : chargePlanList) {\r\n            ChargeRule chargeRule;\r\n            if (chargePlan.getType() == ChargePlanType.FAMILY) {\r\n                chargeRule = ApplicationContextHelper.getBean(FamilyChargeRule.class);\r\n            } else if (chargePlan.getType() == ChargePlanType.FIXED_TIME) {\r\n                chargeRule = ApplicationContextHelper.getBean(FixedTimeChargeRule.class);\r\n            } else {\r\n                chargeRule = ApplicationContextHelper.getBean(BasicChargeRule.class);\r\n            }\r\n            chargeRule.belongsTo(chargePlan);\r\n            chargeRules.add(chargeRule);\r\n        }\r\n        CompositeChargeRule compositeChargeRule = new CompositeChargeRule();\r\n        compositeChargeRule.chargeRules = chargeRules;\r\n        return compositeChargeRule;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargerule/CompositeChargeRule.java",
    "content": "package com.huawei.charging.domain.charge.chargerule;\n\nimport com.huawei.charging.domain.charge.ChargeRecord;\nimport com.huawei.charging.domain.charge.ChargeContext;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * 为了应对套餐组合\n * 组合模式（Composite pattern)\n */\npublic class CompositeChargeRule {\n    public List<ChargeRule> chargeRules;\n\n    public List<ChargeRecord> doCharge(ChargeContext chargeContext){\n        List<ChargeRecord> chargeRecords = new ArrayList<>();\n        for(ChargeRule chargeRule : chargeRules){\n            ChargeRecord chargeRecord = chargeRule.doCharge(chargeContext);\n            if(chargeRecord != null){\n                chargeRecord.setSessionId(chargeContext.getSession().getSessionId());\n                //fill fields for persistence needs\n                chargeRecord.setCreateTime(new Date());\n                chargeRecord.setUpdateTime(new Date());\n                chargeRecords.add(chargeRecord);\n            }\n        }\n        return chargeRecords;\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargerule/FamilyChargeRule.java",
    "content": "package com.huawei.charging.domain.charge.chargerule;\r\n\r\nimport com.huawei.charging.domain.charge.ChargeRecord;\r\nimport com.huawei.charging.domain.charge.ChargeContext;\r\nimport com.huawei.charging.domain.charge.Money;\r\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlanType;\r\nimport com.huawei.charging.domain.charge.chargeplan.FamilyChargePlan;\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport org.springframework.stereotype.Component;\r\n\r\n@Component\r\n@Slf4j\r\npublic class FamilyChargeRule extends AbstractChargeRule {\r\n\r\n    @Override\r\n    public ChargeRecord doCharge(ChargeContext ctx) {\r\n        FamilyChargePlan familyChargePlan = (FamilyChargePlan) chargePlan;\r\n        FamilyChargePlan.FamilyMember familyMember = familyChargePlan.getResource();\r\n        if (familyMember.isMember(ctx.otherSidePhoneNo)) {\r\n            log.debug(\"Family Charge plan for Account : \" + ctx.account);\r\n            ChargeRecord chargeRecord = new ChargeRecord(ctx.phoneNo, ctx.callType, ctx.durationToCharge, ChargePlanType.FAMILY, Money.of(0));\r\n            ctx.setDurationToCharge(0);\r\n            return chargeRecord;\r\n        }\r\n        return null;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/charge/chargerule/FixedTimeChargeRule.java",
    "content": "package com.huawei.charging.domain.charge.chargerule;\r\n\r\nimport com.huawei.charging.domain.charge.ChargeRecord;\r\nimport com.huawei.charging.domain.charge.ChargeContext;\r\nimport com.huawei.charging.domain.charge.Money;\r\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlanType;\r\nimport com.huawei.charging.domain.charge.chargeplan.FixedTimeChangePlan;\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport org.springframework.stereotype.Component;\r\n\r\n@Component\r\n@Slf4j\r\npublic class FixedTimeChargeRule extends AbstractChargeRule {\r\n    @Override\r\n    public ChargeRecord doCharge(ChargeContext ctx) {\r\n        if(!ctx.needCharge()){\r\n            log.debug(\"No need charge for : \"+ctx);\r\n            return null;\r\n        }\r\n        FixedTimeChangePlan fixedTimeChangePlan = (FixedTimeChangePlan) chargePlan;\r\n        FixedTimeChangePlan.FreeCallTime freeCallTime = fixedTimeChangePlan.getResource();\r\n        if (ctx.isCalling() && freeCallTime.isCallingTimeRemaining()) {\r\n            int leftDuration = freeCallTime.chargeFreeCallingTime(ctx.durationToCharge);\r\n            log.debug(\"Calling Left Duration after FixedTimeCharge : \" + leftDuration);\r\n            ChargeRecord chargeRecord = new ChargeRecord(ctx.phoneNo, ctx.callType, ctx.durationToCharge - leftDuration, ChargePlanType.FIXED_TIME, Money.of(0));\r\n            ctx.setDurationToCharge(leftDuration);\r\n            return chargeRecord;\r\n        }\r\n        if (ctx.isCalled() && freeCallTime.isCalledTimeRemaining()) {\r\n            int leftDuration = freeCallTime.chargeFreeCalledTime(ctx.durationToCharge);\r\n            log.debug(\"Called Left Duration after FixedTimeCharge : \" + leftDuration);\r\n            ChargeRecord chargeRecord = new ChargeRecord(ctx.phoneNo, ctx.callType, ctx.durationToCharge - leftDuration, ChargePlanType.FIXED_TIME, Money.of(0));\r\n            ctx.setDurationToCharge(leftDuration);\r\n            return chargeRecord;\r\n        }\r\n        return null;\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/gateway/AccountGateway.java",
    "content": "package com.huawei.charging.domain.gateway;\r\n\r\nimport com.huawei.charging.domain.account.Account;\r\nimport com.huawei.charging.domain.charge.ChargeRecord;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * 跟账户系统交互的网关（Gateway）\r\n *\r\n * @version 1.0\r\n */\r\npublic interface AccountGateway {\r\n\r\n    /**\r\n     * 根据用户号码获取账户信息（含计费项余额等信息）\r\n     *\r\n     * @param phoneNo 电话号码\r\n     * @return 账户信息\r\n     */\r\n    Account getAccount(long phoneNo);\r\n\r\n    /**\r\n     * 将扣费记录同步到账户中\r\n     *\r\n     * @param phoneNo 电话号码\r\n     * @param records 扣费记录\r\n     */\r\n    void sync(long phoneNo, List<ChargeRecord> records);\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/gateway/ChargeGateway.java",
    "content": "package com.huawei.charging.domain.gateway;\n\nimport com.huawei.charging.domain.charge.ChargeRecord;\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.List;\n\n@Repository\npublic interface ChargeGateway extends JpaRepository<ChargeRecord, Long> {\n    public List<ChargeRecord> findBySessionId(String sessionId);\n\n    public ChargeRecord getBySessionId(String sessionId);\n\n    public List<ChargeRecord> findByPhoneNo(long phoneNo);\n}\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/domain/gateway/SessionGateway.java",
    "content": "package com.huawei.charging.domain.gateway;\r\n\r\nimport com.huawei.charging.domain.charge.Session;\r\n\r\npublic interface SessionGateway {\r\n\r\n    void create(Session session);\r\n\r\n    Session get(String sessionId);\r\n\r\n    void end(String sessionId);\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/infrastructure/AccountGatewayImpl.java",
    "content": "package com.huawei.charging.infrastructure;\n\nimport com.huawei.charging.domain.account.Account;\nimport com.huawei.charging.domain.charge.ChargeRecord;\nimport com.huawei.charging.domain.charge.Money;\nimport com.huawei.charging.domain.gateway.AccountGateway;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.client.RestClient;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n@Component\n@Slf4j\npublic class AccountGatewayImpl implements AccountGateway {\n    private static final String GET_ACCOUNT_PATH = \"/v1/api/account/{account}\";\n    private static final String SYNC_ACCOUNT_PATH = \"/v1/api/account/account/{account}/sync\";\n\n    private Map<Long, Account> accountMap = new HashMap<>();\n\n    @Autowired\n    private RestClient restClient;\n\n    @Override\n    public Account getAccount(long phoneNo) {\n        Account account = restClient.get()\n                .uri(GET_ACCOUNT_PATH, phoneNo)\n                .accept(MediaType.APPLICATION_JSON)\n                .retrieve()\n                .body(Account.class);\n\n        return account;\n    }\n\n    @Override\n    public void sync(long phoneNo, List<ChargeRecord> records) {\n        // 更新账户系统\n        log.info(\"sync account info, to be implemented\");\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/infrastructure/RestClientBean.java",
    "content": "package com.huawei.charging.infrastructure;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.client.RestClient;\n\n@Configuration\npublic class RestClientBean {\n\n    @Value(\"${REMOTE_BASE_URI:http://localhost:8080}\")\n    String baseURI;\n\n    @Bean\n    RestClient restClient() {\n        return RestClient.create(baseURI);\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/main/java/com/huawei/charging/infrastructure/SessionGatewayImpl.java",
    "content": "package com.huawei.charging.infrastructure;\r\n\r\nimport com.huawei.charging.domain.BizException;\r\nimport com.huawei.charging.domain.charge.Session;\r\nimport com.huawei.charging.domain.gateway.SessionGateway;\r\nimport org.springframework.stereotype.Component;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\n@Component\r\npublic class SessionGatewayImpl implements SessionGateway {\r\n    private Map<String, Session> sessionMap = new HashMap<>();\r\n\r\n    @Override\r\n    public void create(Session session) {\r\n        sessionMap.put(session.getSessionId(), session);\r\n    }\r\n\r\n    @Override\r\n    public Session get(String sessionId) {\r\n        return sessionMap.get(sessionId);\r\n    }\r\n\r\n    @Override\r\n    public void end(String sessionId) {\r\n        //真实场景是逻辑删除，比如把session的状态标记为“已结束”。\r\n        sessionMap.remove(sessionId);\r\n    }\r\n}\r\n"
  },
  {
    "path": "cola-samples/charge/src/main/resources/application.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://${MYSQL_SERVER:localhost}:${MYSQL_PORT:3306}/${MYSQL_DB_NAME:chargeDB}?serverTimezone=UTC\n    #如果运行出错，可以把连接写成下面的路径进行测试\n    #url: jdbc:mysql://localhost:3306/blogDB?useUnicode=true&characterEncoding=utf-8\n    username: ${MYSQL_USER_TEST:root}\n    password: ${MYSQL_PASSWORD_TEST:root}\n  jpa:\n    hibernate:\n      ddl-auto: update\n    show-sql: true\n\nserver:\n  port: 8081\n\nmy-name: default\nmy-age: default\n"
  },
  {
    "path": "cola-samples/charge/src/main/resources/logback.xml",
    "content": "<configuration>\r\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\r\n\r\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\r\n        <encoder>\r\n            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>\r\n            <charset>utf8</charset>\r\n        </encoder>\r\n    </appender>\r\n\r\n    <!--rootLogger是默认的logger-->\r\n    <root level=\"INFO\">\r\n        <appender-ref ref=\"CONSOLE\"/>\r\n    </root>\r\n\r\n    <!--应用日志-->\r\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\r\n    <logger name=\"com.huawei\" level=\"DEBUG\"/>\r\n\r\n    <!--全局的访问日志-->\r\n    <logger name=\"com.alibaba.cola.catchlog\" level=\"DEBUG\"/>\r\n\r\n</configuration>\r\n"
  },
  {
    "path": "cola-samples/charge/src/test/charge.http",
    "content": "### list charge records by sessionId\nGET http://localhost:8080/123145/chargeRecords\nAccept: application/json\n\n### end session\nPOST http://localhost:8080/session/123145/end?duration=10\nContent-Type: application/x-www-form-urlencoded\n\nduration=10\n\n### do charge\nPOST http://localhost:8080/session/123145/charge?duration=10\n\n\n### begin Session\nPOST http://localhost:8080/session/123145/begin?callingPhoneNo=13681874561&calledPhoneNo=15921252125\n\n<> 2022-11-03T150743.200.json\n\n\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/CleanArchTest.java",
    "content": "package com.huawei.charging;\n\nimport org.junit.jupiter.api.Test;\n\npublic class CleanArchTest {\n    @Test\n    public void protect_clean_arch() {\n//        JavaClasses classes = new ClassFileImporter()\n//                .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)\n//                .importPackages(\"com.huawei.charging\");\n//\n//        layeredArchitecture()\n//                .consideringOnlyDependenciesInLayers()\n//                .layer(\"adapter\").definedBy(\"com.huawei.charging.adapter\")\n//                .layer(\"application\").definedBy(\"com.huawei.charging.application\")\n//                .layer(\"domain\").definedBy(\"com.huawei.charging.domain\")\n//                .layer(\"infrastructure\").definedBy(\"com.huawei.charging.infrastructure\")\n//                .whereLayer(\"adapter\").mayNotBeAccessedByAnyLayer()\n//                //.whereLayer(\"domain\").mayOnlyBeAccessedByLayers(\"application\", \"infrastructure\")\n//                .as(\"The layer dependencies must be respected\")\n//                .because(\"we must follow the Clean Architecture principle\")\n//                .check(classes);\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/TestsContainerBoot.java",
    "content": "package com.huawei.charging;\n\nimport com.alibaba.cola.test.TestsContainer;\n\npublic class TestsContainerBoot {\n    public static void main(String[] args) {\n        TestsContainer.start();\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/application/ChargeServiceTest.java",
    "content": "package com.huawei.charging.application;\n\nimport com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;\nimport com.github.tomakehurst.wiremock.junit5.WireMockTest;\nimport com.huawei.charging.Application;\nimport com.huawei.charging.application.dto.BeginSessionRequest;\nimport com.huawei.charging.domain.BizException;\nimport com.huawei.charging.domain.gateway.AccountGateway;\nimport com.huawei.charging.domain.gateway.SessionGateway;\nimport com.huawei.charging.infrastructure.WireMockRegister;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.ContextConfiguration;\n\n\n\n@SpringBootTest\n@ContextConfiguration(classes = Application.class)\n@WireMockTest(httpPort = 8080)\npublic class ChargeServiceTest {\n\n    @Autowired\n    private ChargeServiceI chargeService;\n\n    @Autowired\n    private SessionGateway sessionGateway;\n\n    @Autowired\n    private AccountGateway accountGateway;\n\n\n    @Test\n    public void test_session_create(WireMockRuntimeInfo wmRuntimeInfo) {\n        WireMockRegister.registerStub(wmRuntimeInfo.getWireMock(), \"/fixture/wiremock/stub_account.json\");\n\n        BeginSessionRequest request = new BeginSessionRequest();\n        String sessionId = \"00002\";\n        request.setSessionId(sessionId);\n        request.setCallingPhoneNo(13681874563L);\n        request.setCalledPhoneNo(15921582125L);\n\n        chargeService.begin(request);\n\n        Assertions.assertEquals(sessionId, sessionGateway.get(sessionId).getSessionId());\n    }\n\n    @Test\n    public void test_remaining_insufficient(WireMockRuntimeInfo wmRuntimeInfo) {\n        WireMockRegister.registerStub(wmRuntimeInfo.getWireMock(), \"/fixture/wiremock/stub_insufficient_account.json\");\n\n        BeginSessionRequest request = new BeginSessionRequest();\n        String sessionId = \"00003\";\n        request.setSessionId(sessionId);\n        request.setCallingPhoneNo(13681874561L);\n        request.setCalledPhoneNo(15921582125L);\n\n        Exception exception = Assertions.assertThrows(BizException.class, () -> {\n            chargeService.begin(request);\n        });\n        String expectedMsg = \"has insufficient amount\";\n        String actualMsg = exception.getMessage();\n        Assertions.assertTrue(actualMsg.contains(expectedMsg));\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/domain/ChargeRecordPlanTest.java",
    "content": "package com.huawei.charging.domain;\n\n\nimport com.huawei.charging.domain.charge.chargeplan.BasicChargePlan;\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlan;\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlanType;\nimport com.huawei.charging.domain.charge.chargeplan.FamilyChargePlan;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class ChargeRecordPlanTest {\n\n    @Test\n    public void test_priority(){\n        ChargePlan basicChargePlan = new BasicChargePlan();\n        ChargePlan familyChargePlan = new FamilyChargePlan();\n        ChargePlan fixedTimeChargePlan = new FamilyChargePlan();\n        List<ChargePlan> chargePlanList =  new ArrayList<>();\n        chargePlanList.add(basicChargePlan);\n        chargePlanList.add(familyChargePlan);\n        chargePlanList.add(fixedTimeChargePlan);\n\n        Collections.sort(chargePlanList);\n\n        System.out.println(chargePlanList.get(0));\n        Assertions.assertEquals(ChargePlanType.FAMILY, chargePlanList.get(0).getType());\n\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/domain/ChargeRecordRuleTest.java",
    "content": "package com.huawei.charging.domain;\n\nimport com.huawei.charging.domain.account.Account;\nimport com.huawei.charging.domain.charge.CallType;\nimport com.huawei.charging.domain.charge.ChargeContext;\nimport com.huawei.charging.domain.charge.Money;\nimport com.huawei.charging.domain.charge.chargeplan.BasicChargePlan;\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlan;\nimport com.huawei.charging.domain.charge.chargeplan.FamilyChargePlan;\nimport com.huawei.charging.domain.charge.chargeplan.FixedTimeChangePlan;\nimport com.huawei.charging.domain.charge.chargerule.BasicChargeRule;\nimport com.huawei.charging.domain.charge.chargerule.FamilyChargeRule;\nimport com.huawei.charging.domain.charge.chargerule.FixedTimeChargeRule;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collections;\n\npublic class ChargeRecordRuleTest {\n\n    @Test\n    public void test_basic_charge_rule(){\n        //prepare\n        ChargePlan chargePlan = new BasicChargePlan();\n        Account account = new Account(13681874561L, Money.of(200), Collections.singletonList(chargePlan));\n        ChargeContext ctx = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 20);\n        ctx.account = account;\n        System.out.println(\"Account before charge: \"+ account);\n\n        //do\n        BasicChargeRule basicChargeRule = new BasicChargeRule();\n        basicChargeRule.belongsTo(chargePlan);\n        basicChargeRule.doCharge(ctx);\n\n        //check\n        System.out.println(\"Account after charge: \"+ account);\n        Assertions.assertEquals( Money.of(100), ctx.account.getRemaining());\n        Assertions.assertEquals( 0, ctx.getDurationToCharge());\n    }\n\n    @Test\n    public void test_family_charge_rule(){\n        //prepare\n        FamilyChargePlan chargePlan = new FamilyChargePlan();\n        Account account = new Account(13681874561L, Money.of(200), Collections.singletonList(chargePlan));\n        ChargeContext ctx = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 20);\n        ctx.account = account;\n        System.out.println(\"Account before charge: \"+ account);\n\n        //do\n        FamilyChargeRule familyChargeRule = new FamilyChargeRule();\n        familyChargeRule.belongsTo(chargePlan);\n        familyChargeRule.doCharge(ctx);\n\n        //check\n        System.out.println(\"Account after charge: \"+ account);\n        Assertions.assertEquals( Money.of(200), ctx.account.getRemaining());\n        Assertions.assertEquals( 0, ctx.getDurationToCharge());\n    }\n\n    @Test\n    public void test_fixed_time_charge_rule(){\n        //prepare\n        FixedTimeChangePlan chargePlan = new FixedTimeChangePlan();\n        Account account = new Account(13681874561L, Money.of(200), Collections.singletonList(chargePlan));\n        ChargeContext ctx = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 180);\n        ctx.account = account;\n        System.out.println(\"Account before charge: \"+ account);\n\n        //do\n        FixedTimeChargeRule fixedTimeChargeRule = new FixedTimeChargeRule();\n        fixedTimeChargeRule.belongsTo(chargePlan);\n        fixedTimeChargeRule.doCharge(ctx);\n\n        //check\n        System.out.println(\"Account after charge: \"+ account);\n        Assertions.assertEquals( true, chargePlan.getResource().isCallingTimeRemaining());\n        Assertions.assertEquals( 0, ctx.getDurationToCharge());\n\n        // come a new charge\n        ChargeContext ctx2 = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 40);\n        ctx2.account = account;\n        fixedTimeChargeRule.doCharge(ctx2);\n        Assertions.assertEquals( false, chargePlan.getResource().isCallingTimeRemaining());\n        Assertions.assertEquals( 20, ctx2.getDurationToCharge());\n\n        //reset fixed time\n        FixedTimeChangePlan.FreeCallTime.FREE_CALLED_TIME = 200;\n        FixedTimeChangePlan.FreeCallTime.FREE_CALLING_TIME = 200;\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/domain/CompositeChargeRuleTestRecord.java",
    "content": "package com.huawei.charging.domain;\n\nimport com.huawei.charging.Application;\nimport com.huawei.charging.domain.account.Account;\nimport com.huawei.charging.domain.charge.*;\nimport com.huawei.charging.domain.charge.chargeplan.BasicChargePlan;\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlan;\nimport com.huawei.charging.domain.charge.chargeplan.FamilyChargePlan;\nimport com.huawei.charging.domain.charge.chargeplan.FixedTimeChangePlan;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.ContextConfiguration;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\n@SpringBootTest\n@ContextConfiguration(classes = Application.class)\npublic class CompositeChargeRuleTestRecord {\n\n    private long callingPhoneNo = 13681874561L;\n    private long calledPhoneNo = 15921582125L;\n\n    @Test\n    public void test_basic_and_fixedTime_charge_rule(){\n        // prepare\n        List<ChargePlan> chargePlanList = new ArrayList<>();\n        chargePlanList.add(new BasicChargePlan());\n        chargePlanList.add(new FixedTimeChangePlan());\n        Account account = Account.valueOf(13681874561L, Money.of(200)); // for spring bean\n        account.setChargePlanList(chargePlanList);\n        ChargeContext ctx = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 220);\n        String sessionId = UUID.randomUUID().toString();\n        Session session = new Session(sessionId, callingPhoneNo, calledPhoneNo);\n        ctx.setSession(session);\n        ctx.account = account;\n\n        // do\n        List<ChargeRecord> chargeRecords = account.charge(ctx);\n        System.out.println(\"Account after charge: \"+ account);\n        // check\n        Assertions.assertEquals(2, chargeRecords.size());\n    }\n\n    @Test\n    public void test_basic_and_family_charge_rule(){\n        // prepare\n        List<ChargePlan> chargePlanList = new ArrayList<>();\n        chargePlanList.add(new BasicChargePlan());\n        chargePlanList.add(new FamilyChargePlan());\n        Account account = Account.valueOf(13681874561L, Money.of(200)); // for spring bean\n        account.setChargePlanList(chargePlanList);\n        ChargeContext ctx = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 220);\n        String sessionId = UUID.randomUUID().toString();\n        Session session = new Session(sessionId, callingPhoneNo, calledPhoneNo);\n        ctx.setSession(session);\n        ctx.account = account;\n\n        // do\n        List<ChargeRecord> chargeRecords = account.charge(ctx);\n        System.out.println(\"Account after charge: \"+ account);\n        // check\n        Assertions.assertEquals(1, chargeRecords.size());\n    }\n\n    @Test\n    public void test_all_charge_rule(){\n        // prepare\n        List<ChargePlan> chargePlanList = new ArrayList<>();\n        chargePlanList.add(new BasicChargePlan());\n        chargePlanList.add(new FamilyChargePlan());\n        chargePlanList.add(new FixedTimeChangePlan());\n        Account account = Account.valueOf(13681874561L, Money.of(200)); // for spring bean\n        account.setChargePlanList(chargePlanList);\n        ChargeContext ctx = new ChargeContext(CallType.CALLING, 13681874561L, 15921582125L, 220);\n        String sessionId = UUID.randomUUID().toString();\n        Session session = new Session(sessionId, callingPhoneNo, calledPhoneNo);\n        ctx.setSession(session);\n        ctx.account = account;\n\n        // do\n        List<ChargeRecord> chargeRecords = account.charge(ctx);\n        System.out.println(\"Account after charge: \"+ account);\n\n        // check\n        Assertions.assertEquals(1, chargeRecords.size());\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/infrastructure/AccountGatewayTest.java",
    "content": "package com.huawei.charging.infrastructure;\n\nimport com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;\nimport com.github.tomakehurst.wiremock.junit5.WireMockTest;\nimport com.huawei.charging.Application;\nimport com.huawei.charging.domain.account.Account;\nimport com.huawei.charging.domain.charge.Money;\nimport com.huawei.charging.domain.gateway.AccountGateway;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.ContextConfiguration;\n\n@SpringBootTest\n@ContextConfiguration(classes = Application.class)\n@WireMockTest(httpPort = 8080)\npublic class AccountGatewayTest {\n\n    @Autowired\n    AccountGateway accountGateway;\n\n    @Test\n    public void testGetAccount(WireMockRuntimeInfo wmRuntimeInfo) {\n        WireMockRegister.registerStub(wmRuntimeInfo.getWireMock(), \"/fixture/wiremock/stub_account.json\");\n\n        Account account = accountGateway.getAccount(15921582125L);\n        System.out.println(\"account : \" + account);\n\n        Assertions.assertEquals(account.getPhoneNo(), 15921582125L);\n        Assertions.assertEquals(account.getRemaining(), Money.of(400));\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/infrastructure/ChargeRecordRepoTest.java",
    "content": "package com.huawei.charging.infrastructure;\n\nimport com.huawei.charging.domain.charge.CallType;\nimport com.huawei.charging.domain.charge.ChargeRecord;\nimport com.huawei.charging.domain.charge.Money;\nimport com.huawei.charging.domain.charge.chargeplan.ChargePlanType;\nimport com.huawei.charging.domain.gateway.ChargeGateway;\n\nimport jakarta.annotation.Resource;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.UUID;\n\n@SpringBootTest\npublic class ChargeRecordRepoTest {\n    @Resource\n    private ChargeGateway chargeGateway;\n\n    private String sessionId;\n\n    @BeforeEach\n    public void setup(){\n        sessionId = UUID.randomUUID().toString();\n    }\n\n    @Test\n    public void testSave(){\n        ChargeRecord chargeRecord = new ChargeRecord(13681874561L, CallType.CALLED, 10, ChargePlanType.FAMILY, Money.of(123));\n        chargeRecord.setSessionId(sessionId);\n        chargeRecord.setCreateTime(new Date());\n        chargeRecord.setUpdateTime(new Date());\n        chargeGateway.save(chargeRecord);\n\n        chargeRecord = chargeGateway.getBySessionId(sessionId);\n\n        Assertions.assertEquals(chargeRecord.getSessionId(), sessionId);\n    }\n\n    @Test\n    public void testSaveList(){\n        List<ChargeRecord> chargeRecordList = new ArrayList<>();\n        ChargeRecord chargeRecord1 = new ChargeRecord(13681874561L, CallType.CALLED, 10, ChargePlanType.FAMILY, Money.of(123));\n        chargeRecord1.setSessionId(UUID.randomUUID().toString());\n        ChargeRecord chargeRecord2 = new ChargeRecord(13681874561L, CallType.CALLING, 10, ChargePlanType.FAMILY, Money.of(123));\n        chargeRecord2.setSessionId(UUID.randomUUID().toString());\n        chargeRecordList.add(chargeRecord1);\n        chargeRecordList.add(chargeRecord2);\n        chargeGateway.saveAll(chargeRecordList);\n\n        List<ChargeRecord> result = chargeGateway.findByPhoneNo(13681874561L);\n\n        Assertions.assertEquals(result.size(), 2);\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/infrastructure/FixtureLoader.java",
    "content": "package com.huawei.charging.infrastructure;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UncheckedIOException;\nimport java.nio.charset.StandardCharsets;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.util.StreamUtils;\n\npublic class FixtureLoader {\n\n    public static String loadResource(String resourcePath) {\n        // 创建一个 ClassPathResource 对象\n        ClassPathResource resource = new ClassPathResource(resourcePath);\n\n        // 使用 withResource 来自动关闭输入流\n        String content = \"\";\n        try (InputStream inputStream = resource.getInputStream()) {\n            content = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);\n        } catch (IOException e) {\n            e.printStackTrace();\n            throw new RuntimeException(e.getMessage());\n        }\n        return content;\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/infrastructure/JSONTest.java",
    "content": "package com.huawei.charging.infrastructure;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.huawei.charging.domain.account.Account;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\npublic class JSONTest {\n\n    @Test\n    public void testJsonBind() {\n        // this will throw exception since account not recognized\n        String badJson = \"{\\\"account\\\":{\\\"name\\\":\\\"frank\\\",\\\"phoneNo\\\":\\\"15921582125\\\",\\\"remaining\\\":\\\"400\\\",\\\"chargePlanList\\\":[{\\\"priority\\\":\\\"2\\\",\\\"type\\\":\\\"fixedTime\\\"},{\\\"priority\\\":\\\"1\\\",\\\"type\\\":\\\"familyMember\\\"}]}}\";\n        // this is good\n        String goodJson = \"{\\\"name\\\":\\\"frank\\\",\\\"phoneNo\\\":\\\"15921582125\\\",\\\"remaining\\\":\\\"400\\\",\\\"chargePlanList\\\":[{\\\"priority\\\":\\\"2\\\",\\\"type\\\":\\\"fixedTime\\\"},{\\\"priority\\\":\\\"1\\\",\\\"type\\\":\\\"familyMember\\\"}]}\";\n\n        try {\n            ObjectMapper objectMapper = new ObjectMapper();\n            Account account = objectMapper.readValue(goodJson, Account.class);\n            Assertions.assertEquals(account.getPhoneNo(), 15921582125L);\n            System.out.println(account);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/infrastructure/SpingBootConfTest.java",
    "content": "package com.huawei.charging.infrastructure;\n\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.ActiveProfiles;\n\n\n@SpringBootTest\n//use test profile, this will merge application.yml and application-test.yaml.\n//but if you put an application.yml under test/resources, it will replace the project application.yml.\n//the advantage of profile, is that it can inherit and override.\n@ActiveProfiles(\"test\")\npublic class SpingBootConfTest {\n\n    @Value(\"${spring.jpa.show-sql}\")\n    private String showSql;\n\n    @Value(\"${spring.jpa.hibernate.ddl-auto}\")\n    private String ddlAuto;\n\n    @Value(\"${my-name}\")\n    private String myName;\n\n    @Value(\"${my-age}\")\n    private String myAge;\n\n    @Value(\"${my-age-test}\")\n    private String myAgeTest;\n\n\n    @Test\n    public void test() {\n        System.out.println(\"spring.jpa.show-sql : \" + showSql);\n        System.out.println(\"spring.jpa.hibernate.ddl-auto : \" + ddlAuto);\n        System.out.println(\"myName : \" + myName);\n        System.out.println(\"myAge : \" + myAge);\n        System.out.println(\"myAgeTest : \" + myAgeTest);\n\n        Assertions.assertEquals(\"30\", myAge);\n        Assertions.assertEquals(\"40\", myAgeTest);\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/infrastructure/WireMockBasicTest.java",
    "content": "package com.huawei.charging.infrastructure;\n\n\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;\nimport com.github.tomakehurst.wiremock.junit5.WireMockTest;\nimport com.huawei.charging.Application;\nimport com.huawei.charging.domain.account.Account;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.http.MediaType;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\n\n@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@WireMockTest(httpPort = 8080)\n@Slf4j\npublic class WireMockBasicTest {\n\n    @Autowired\n    protected WebTestClient webClient;\n\n    @Test\n    public void testWireMockBasic() {\n        // The static DSL will be automatically configured for you\n        stubFor(get(\"/static-dsl\").willReturn(ok()));\n\n        webClient.get()\n                .uri(\"http://localhost:8080/static-dsl\")\n                .exchange()\n                .expectStatus()\n                .isEqualTo(200);\n    }\n\n    @Test\n    public void testWireMockStub(WireMockRuntimeInfo wmRuntimeInfo) {\n        WireMock wireMock = wmRuntimeInfo.getWireMock();\n        WireMockRegister.registerStub(wireMock, \"/fixture/wiremock/stub_wire_mock_basic.json\");\n\n        webClient.get()\n                .uri(\"http://localhost:8080/v1/wiremock/basic\")\n                .exchange()\n                .expectStatus()\n                .isEqualTo(200)\n                .expectHeader()\n                .contentType(MediaType.APPLICATION_JSON);\n\n        System.out.println(\"wire mock serer port : \" + wmRuntimeInfo.getHttpPort());\n    }\n\n    @Test\n    public void testWireMockAccount(WireMockRuntimeInfo wmRuntimeInfo) {\n        WireMockRegister.registerStub(wmRuntimeInfo.getWireMock(), \"/fixture/wiremock/stub_account.json\");\n\n        long phoneNo = 123456789;\n\n        webClient.get()\n                .uri(\"http://localhost:8080/v1/api/account/\"+phoneNo)\n                .exchange()\n                .expectStatus()\n                .isEqualTo(200)\n                .expectHeader()\n                .contentType(MediaType.APPLICATION_JSON)\n                .returnResult(Account.class)\n                .getResponseBody()\n                .map(account -> {\n                    log.info(account.toString());\n                    Assertions.assertEquals(\"frank\", account.getName());\n                    Assertions.assertEquals(phoneNo, account.getPhoneNo());\n                    return account;\n                })\n                .subscribe();\n\n        log.info(\"wire mock serer port : \" + wmRuntimeInfo.getHttpPort());\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/java/com/huawei/charging/infrastructure/WireMockRegister.java",
    "content": "package com.huawei.charging.infrastructure;\n\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wiremock.stubbing.StubMapping;\n\npublic class WireMockRegister {\n\n    public static void registerStub(WireMock wireMock, String resourcePath){\n        StubMapping stubMapping = StubMapping.buildFrom(FixtureLoader.loadResource(resourcePath));\n        wireMock.register(stubMapping);\n    }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/resources/application-test.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    # hard code for test purpose\n    url: jdbc:mysql://localhost:3306/chargeDB?serverTimezone=UTC\n    username: root\n    password: root\n\n  jpa:\n    hibernate:\n      ddl-auto: update\n    show-sql: true\n\n# this will override config in test/resources/application.yml and resources/application.yml\nmy-age: 30\n\nmy-age-test: 40\n"
  },
  {
    "path": "cola-samples/charge/src/test/resources/application.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: org.h2.Driver\n    url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=1\n    username: sa\n    password:\n\n  jpa:\n    hibernate:\n      ddl-auto: update\n    show-sql: true\n\nserver:\n  port: 8081\n\nmy-name: frank\nmy-age: 35\nREMOTE_BASE_URI: http://localhost:8080\n\n\n"
  },
  {
    "path": "cola-samples/charge/src/test/resources/fixture/wiremock/stub_account.json",
    "content": "{\n  \"request\": {\n    \"urlPathPattern\": \"/v1/api/account/[0-9]+\",\n    \"method\": \"GET\"\n  },\n  \"response\": {\n    \"status\": 200,\n    \"headers\": {\n      \"Content-Type\": \"application/json\"\n    },\n    \"transformers\": [\n      \"response-template\"\n    ],\n    \"jsonBody\": {\n      \"name\": \"frank\",\n      \"phoneNo\": \"{{request.path.[3]}}\",\n      \"remaining\": \"400\",\n      \"chargePlanList\": [\n        {\n          \"priority\": \"2\",\n          \"type\": \"fixedTime\"\n        },\n        {\n          \"priority\": \"1\",\n          \"type\": \"familyMember\"\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/resources/fixture/wiremock/stub_insufficient_account.json",
    "content": "{\n  \"request\": {\n    \"urlPathPattern\": \"/v1/api/account/[0-9]+\",\n    \"method\": \"GET\"\n  },\n  \"response\": {\n    \"status\": 200,\n    \"headers\": {\n      \"Content-Type\": \"application/json\"\n    },\n    \"transformers\": [\n      \"response-template\"\n    ],\n    \"jsonBody\": {\n      \"name\": \"frank\",\n      \"phoneNo\": \"{{request.path.[3]}}\",\n      \"remaining\": \"0\",\n      \"chargePlanList\": [\n        {\n          \"priority\": \"2\",\n          \"type\": \"fixedTime\"\n        },\n        {\n          \"priority\": \"1\",\n          \"type\": \"familyMember\"\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/resources/fixture/wiremock/stub_wire_mock_basic.json",
    "content": "{\n  \"request\": {\n    \"urlPathPattern\": \"/v1/wiremock/basic\",\n    \"method\": \"GET\"\n  },\n  \"response\": {\n    \"status\": 200,\n    \"headers\": {\n      \"Content-Type\": \"application/json\"\n    },\n    \"jsonBody\": {\n      \"request_id\": \"f7f9e747-f073-4ea8-8360-b42fc754a049\",\n      \"hyper_switch\": {\n        \"id\": \"switch1\",\n        \"name\": \"1520-001\",\n        \"device_model\": \"1520\",\n        \"role\": \"tor\",\n        \"mgmt_ip\": \"192.168.0.1\",\n        \"rack_code\": \"kw14b2-1k-01-08\",\n        \"sn\": \"21980119523GP8000745\",\n        \"node_id\": \"node1\",\n        \"xpod_id\": \"pod1\",\n        \"op_status\": \"online\",\n        \"created_at\": \"2024-02-04 15:11:13\",\n        \"updated_at\": \"2020-02-04 15:11:13\",\n        \"type\": \"l1\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "cola-samples/charge/src/test/resources/logback-test.xml",
    "content": "<configuration>\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%date{HH:mm:ss} %highlight(%-5level) [%blue(%t)] %yellow(%C{35}): %msg%n%throwable</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!--rootLogger是默认的logger-->\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\"/>\n    </root>\n\n    <!--应用日志-->\n    <!--这个logger没有指定appender，它会继承root节点中定义的那些appender-->\n    <logger name=\"com.huawei\" level=\"DEBUG\"/>\n    <!--hibernate6以上的配置，SQL参数绑定日志-->\n    <logger name=\"org.hibernate.SQL\" level=\"debug\"/>\n    <logger name=\"org.hibernate.orm.jdbc.bind\" level=\"trace\"/>\n\n    <!--全局的访问日志-->\n    <logger name=\"com.alibaba.cola.catchlog\" level=\"DEBUG\"/>\n\n</configuration>\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-adapter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>com.alibaba.craftsman</groupId>\n        <artifactId>craftsman.all</artifactId>\n        <version>1.0.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>craftsman-adapter</artifactId>\n    <packaging>jar</packaging>\n    <name>craftsman-adapter</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.alibaba.craftsman</groupId>\n            <artifactId>craftsman-app</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-adapter/src/main/java/com/alibaba/craftsman/web/MetricsController.java",
    "content": "package com.alibaba.craftsman.web;\n\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.api.MetricsServiceI;\nimport com.alibaba.craftsman.dto.ATAMetricAddCmd;\nimport com.alibaba.craftsman.dto.ATAMetricQry;\nimport com.alibaba.craftsman.dto.clientobject.ATAMetricCO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\npublic class MetricsController {\n\n    @Autowired\n    private MetricsServiceI metricsService;\n\n    @GetMapping(value = \"/metrics/ata\")\n    public MultiResponse<ATAMetricCO> listATAMetrics(@RequestParam String ownerId){\n        ATAMetricQry ataMetricQry = new ATAMetricQry();\n        ataMetricQry.setOwnerId(ownerId);\n        return metricsService.listATAMetrics(ataMetricQry);\n    }\n\n    @PostMapping(value = \"/metrics/ata\")\n    public Response addATAMetric(@RequestBody ATAMetricAddCmd ataMetricAddCmd){\n        return metricsService.addATAMetric(ataMetricAddCmd);\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>com.alibaba.craftsman</groupId>\n        <artifactId>craftsman.all</artifactId>\n        <version>1.0.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>craftsman-app</artifactId>\n    <packaging>jar</packaging>\n    <name>craftsman-app</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.alibaba.craftsman</groupId>\n            <artifactId>craftsman-client</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.craftsman</groupId>\n            <artifactId>craftsman-infrastructure</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-catchlog-starter</artifactId>\n        </dependency>\n\n        <!-- JSR 303 Validation -->\n        <dependency>\n            <groupId>org.hibernate.validator</groupId>\n            <artifactId>hibernate-validator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>javax.el</groupId>\n            <artifactId>javax.el-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.glassfish</groupId>\n            <artifactId>jakarta.el</artifactId>\n        </dependency>\n        <!-- JSR 303 Validation End-->\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/ATAMetricAddCmdExe.java",
    "content": "package com.alibaba.craftsman.command;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.ATAMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.ATAMetricItem;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.InfluenceMetric;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.craftsman.dto.ATAMetricAddCmd;\nimport com.alibaba.craftsman.domain.gateway.MetricGateway;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * ATAMetricAddCmdExe\n *\n * @author Frank Zhang\n * @date 2019-03-01 11:42 AM\n */\n@Component\npublic class ATAMetricAddCmdExe{\n\n    @Autowired\n    private MetricGateway metricGateway;\n\n    public Response execute(ATAMetricAddCmd cmd) {\n        ATAMetricItem ataMetricItem = new ATAMetricItem();\n        BeanUtils.copyProperties(cmd.getAtaMetricCO(), ataMetricItem);\n        ataMetricItem.setSubMetric(new ATAMetric(new InfluenceMetric(new UserProfile(cmd.getAtaMetricCO().getOwnerId()))));\n        metricGateway.save(ataMetricItem);\n        return Response.buildSuccess();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/CodeReviewMetricAddCmdExe.java",
    "content": "package com.alibaba.craftsman.command;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.CodeReviewMetric;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.CodeReviewMetricItem;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.ContributionMetric;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.craftsman.dto.CodeReviewMetricAddCmd;\nimport com.alibaba.craftsman.domain.gateway.MetricGateway;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * CodeReviewMetricAddCmdExe\n *\n * @author Frank Zhang\n * @date 2019-03-04 11:14 AM\n */\n@Component\npublic class CodeReviewMetricAddCmdExe{\n\n    @Autowired\n    private MetricGateway metricGateway;\n\n    public Response execute(CodeReviewMetricAddCmd cmd) {\n        CodeReviewMetricItem codeReviewMetricItem = new CodeReviewMetricItem();\n        BeanUtils.copyProperties(cmd, codeReviewMetricItem);\n        codeReviewMetricItem.setSubMetric(new CodeReviewMetric(new ContributionMetric(new UserProfile(cmd.getOwnerId()))));\n        metricGateway.save(codeReviewMetricItem);\n        return Response.buildSuccess();\n    }\n}"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/MetricDeleteCmdExe.java",
    "content": "package com.alibaba.craftsman.command;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.domain.gateway.MetricGateway;\nimport com.alibaba.craftsman.dto.MetricDeleteCmd;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * MetricDeleteCmdExe\n *\n * @author Frank Zhang\n * @date 2019-03-04 3:01 PM\n */\n@Component\npublic class MetricDeleteCmdExe{\n\n    @Resource\n    private MetricGateway metricGateway;\n\n    public Response execute(MetricDeleteCmd cmd) {\n\n        metricGateway.delete(cmd.getMetricId(), cmd.getOperater());\n\n        return Response.buildSuccess();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/MiscMetricAddCmdExe.java",
    "content": "package com.alibaba.craftsman.command;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.ContributionMetric;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.MiscMetric;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.MiscMetricItem;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.craftsman.dto.MiscMetricAddCmd;\nimport com.alibaba.craftsman.domain.gateway.MetricGateway;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * MiscMetricAddCmdExe\n *\n * @author Frank Zhang\n * @date 2019-03-04 11:15 AM\n */\n@Component\npublic class MiscMetricAddCmdExe{\n\n    @Resource\n    private MetricGateway metricGateway;\n\n    public Response execute(MiscMetricAddCmd cmd) {\n        MiscMetricItem miscMetricItem = new MiscMetricItem();\n        BeanUtils.copyProperties(cmd.getMiscMetricCO(), miscMetricItem);\n        miscMetricItem.setSubMetric(new MiscMetric(new ContributionMetric(new UserProfile(cmd.getMiscMetricCO().getOwnerId()))));\n        metricGateway.save(miscMetricItem);\n        return Response.buildSuccess();\n    }\n}"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/PaperMetricAddCmdExe.java",
    "content": "package com.alibaba.craftsman.command;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.InfluenceMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.PaperMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.PaperMetricItem;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.craftsman.dto.PaperMetricAddCmd;\nimport com.alibaba.craftsman.domain.gateway.MetricGateway;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * PaperMetricAddCmdExe\n *\n * @author Frank Zhang\n * @date 2019-03-03 11:41 AM\n */\n@Component\npublic class PaperMetricAddCmdExe {\n\n    @Autowired\n    private MetricGateway metricGateway;\n\n    public Response execute(PaperMetricAddCmd cmd) {\n        PaperMetricItem paperMetricItem = new PaperMetricItem();\n        BeanUtils.copyProperties(cmd.getPaperMetricCO(), paperMetricItem);\n        paperMetricItem.setSubMetric(new PaperMetric(new InfluenceMetric(new UserProfile(cmd.getPaperMetricCO().getOwnerId()))));\n        paperMetricItem.setMetricOwner(new UserProfile(cmd.getPaperMetricCO().getOwnerId()));\n        metricGateway.save(paperMetricItem);\n\n        return Response.buildSuccess();\n    }\n}"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/PatentMetricAddCmdExe.java",
    "content": "package com.alibaba.craftsman.command;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.AuthorType;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.InfluenceMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.PatentMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.PatentMetricItem;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.craftsman.dto.PatentMetricAddCmd;\nimport com.alibaba.craftsman.domain.gateway.MetricGateway;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * PatentMetricAddCmdExe\n *\n * @author Frank Zhang\n * @date 2019-03-03 11:41 AM\n */\n@Component\npublic class PatentMetricAddCmdExe{\n\n    @Resource\n    private MetricGateway metricGateway;\n\n    public Response execute(PatentMetricAddCmd cmd) {\n        PatentMetricItem patentMetricItem = new PatentMetricItem();\n        BeanUtils.copyProperties(cmd.getPatentMetricCO(), patentMetricItem);\n        patentMetricItem.setSubMetric(new PatentMetric(new InfluenceMetric(new UserProfile(cmd.getPatentMetricCO().getOwnerId()))));\n        patentMetricItem.setAuthorType(AuthorType.valueOf(cmd.getPatentMetricCO().getAuthorType()));\n        metricGateway.save(patentMetricItem);\n        return Response.buildSuccess();\n    }\n}"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/RefactoringMetricAddCmdExe.java",
    "content": "package com.alibaba.craftsman.command;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.ContributionMetric;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.RefactoringLevel;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.RefactoringMetric;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.RefactoringMetricItem;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.craftsman.dto.RefactoringMetricAddCmd;\nimport com.alibaba.craftsman.domain.gateway.MetricGateway;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * RefactoringMetricAddCmdExe\n *\n * @author Frank Zhang\n * @date 2019-03-04 11:15 AM\n */\n@Component\npublic class RefactoringMetricAddCmdExe{\n\n    @Autowired\n    private MetricGateway metricGateway;\n\n    public Response execute(RefactoringMetricAddCmd cmd) {\n        RefactoringMetricItem refactoringMetricItem = new RefactoringMetricItem();\n        BeanUtils.copyProperties(cmd.getRefactoringMetricCO(), refactoringMetricItem);\n        refactoringMetricItem.setSubMetric(new RefactoringMetric(new ContributionMetric(new UserProfile(cmd.getRefactoringMetricCO().getOwnerId()))));\n        refactoringMetricItem.setRefactoringLevel(RefactoringLevel.valueOf(cmd.getRefactoringMetricCO().getRefactoringLevel()));\n        metricGateway.save(refactoringMetricItem);\n        return Response.buildSuccess();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/RefreshScoreCmdExe.java",
    "content": "package com.alibaba.craftsman.command;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.exception.Assert;\nimport com.alibaba.craftsman.domain.metrics.SubMetric;\nimport com.alibaba.craftsman.domain.metrics.appquality.AppMetric;\nimport com.alibaba.craftsman.domain.metrics.appquality.AppQualityMetric;\nimport com.alibaba.craftsman.domain.metrics.devquality.BugMetric;\nimport com.alibaba.craftsman.domain.metrics.devquality.DevQualityMetric;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.ContributionMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.InfluenceMetric;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.craftsman.dto.RefreshScoreCmd;\nimport com.alibaba.craftsman.domain.gateway.MetricGateway;\nimport com.alibaba.craftsman.domain.gateway.UserProfileGateway;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.List;\n\n@Component\npublic class RefreshScoreCmdExe{\n\n    @Resource\n    private UserProfileGateway userProfileGateway;\n\n    @Resource\n    private MetricGateway metricGateway;\n\n    public Response execute(RefreshScoreCmd cmd) {\n        UserProfile userProfile = getUserProfile(cmd);\n        calculateScore(userProfile);\n        update(userProfile);\n        return Response.buildSuccess();\n    }\n\n    private UserProfile getUserProfile(RefreshScoreCmd cmd) {\n        UserProfile userProfile = userProfileGateway.getByUserId(cmd.getUserId());\n        Assert.notNull(userProfile, \"There is no User Profile for \"+cmd.getUserId()+\" to update\");\n        return userProfile;\n    }\n\n    private void calculateScore(UserProfile userProfile) {\n        loadInfluenceMetric(userProfile);\n        loadContributionMetrics(userProfile);\n        loadDevQualityMetrics(userProfile);\n        loadAppQualityMetrics(userProfile);\n        userProfile.calculateScore();\n    }\n\n    private void loadAppQualityMetrics(UserProfile userProfile) {\n        AppQualityMetric appQualityMetric = new AppQualityMetric(userProfile);\n        AppMetric appMetric = metricGateway.getAppMetric(userProfile.getUserId());\n        appMetric.setParent(appQualityMetric);\n    }\n\n    private void loadDevQualityMetrics(UserProfile userProfile) {\n        DevQualityMetric devQualityMetric = new DevQualityMetric(userProfile);\n        BugMetric bugMetric = metricGateway.getBugMetric(userProfile.getUserId());\n        bugMetric.setParent(devQualityMetric);\n    }\n\n    private void loadContributionMetrics(UserProfile userProfile) {\n        ContributionMetric contributionMetric = new ContributionMetric(userProfile);\n        List<SubMetric> subMetricList = metricGateway.listByTechContribution(userProfile.getUserId());\n        subMetricList.forEach(subMetric -> subMetric.setParent(contributionMetric));\n    }\n\n    private void loadInfluenceMetric(UserProfile userProfile) {\n        InfluenceMetric influenceMetric = new InfluenceMetric(userProfile);\n        List<SubMetric> subMetricList = metricGateway.listByTechInfluence(userProfile.getUserId());\n        subMetricList.forEach(subMetric -> subMetric.setParent(influenceMetric));\n    }\n\n    private void update(UserProfile userProfile) {\n        userProfileGateway.update(userProfile);\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/SharingMetricAddCmdExe.java",
    "content": "package com.alibaba.craftsman.command;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.InfluenceMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.SharingMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.SharingMetricItem;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.SharingScope;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.craftsman.dto.SharingMetricAddCmd;\nimport com.alibaba.craftsman.domain.gateway.MetricGateway;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * SharingMetricAddCmdExe\n *\n * @author Frank Zhang\n * @date 2019-03-02 5:00 PM\n */\n@Component\npublic class SharingMetricAddCmdExe{\n\n    @Resource\n    private MetricGateway metricGateway;\n\n    public Response execute(SharingMetricAddCmd cmd) {\n        SharingMetricItem sharingMetricItem = new SharingMetricItem();\n        BeanUtils.copyProperties(cmd.getSharingMetricCO(), sharingMetricItem);\n        sharingMetricItem.setSubMetric(new SharingMetric(new InfluenceMetric(new UserProfile(cmd.getSharingMetricCO().getOwnerId()))));\n        sharingMetricItem.setSharingScope(SharingScope.valueOf(cmd.getSharingMetricCO().getSharingScope()));\n        metricGateway.save(sharingMetricItem);\n        return Response.buildSuccess();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/UserProfileAddCmdExe.java",
    "content": "package com.alibaba.craftsman.command;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.convertor.UserProfileConvertor;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.craftsman.dto.UserProfileAddCmd;\nimport com.alibaba.craftsman.domain.gateway.UserProfileGateway;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * UserProfileAddCmdExe\n *\n * @author Frank Zhang\n * @date 2019-02-28 6:25 PM\n */\n@Component\npublic class UserProfileAddCmdExe{\n\n    @Resource\n    private UserProfileGateway userProfileGateway;\n\n    public Response execute(UserProfileAddCmd cmd) {\n        UserProfile userProfile = UserProfileConvertor.toEntity(cmd.getUserProfileCO());\n        userProfileGateway.create(userProfile);\n        return Response.buildSuccess();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/UserProfileUpdateCmdExe.java",
    "content": "package com.alibaba.craftsman.command;\n\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.convertor.UserProfileConvertor;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.craftsman.dto.UserProfileUpdateCmd;\nimport com.alibaba.craftsman.domain.gateway.UserProfileGateway;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n@Component\npublic class UserProfileUpdateCmdExe{\n\n    @Resource\n    private UserProfileGateway userProfileGateway;\n\n    public Response execute(UserProfileUpdateCmd cmd) {\n        UserProfile userProfile = UserProfileConvertor.toEntity(cmd.getUserProfileCO());\n        userProfileGateway.update(userProfile);\n        return Response.buildSuccess();\n    }\n}"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/package-info.java",
    "content": "/**\n * This package contains CommandExecutors which are used to process Command Request.\n * \n * @author fulan.zjf\n */\npackage com.alibaba.craftsman.command;"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/query/ATAMetricQryExe.java",
    "content": "package com.alibaba.craftsman.command.query;\n\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.craftsman.domain.metrics.SubMetricType;\nimport com.alibaba.craftsman.dto.ATAMetricQry;\nimport com.alibaba.craftsman.dto.clientobject.ATAMetricCO;\nimport com.alibaba.craftsman.gatewayimpl.database.MetricMapper;\nimport com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO;\nimport com.alibaba.fastjson.JSON;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Component\npublic class ATAMetricQryExe{\n\n    @Resource\n    private MetricMapper metricMapper;\n\n    public MultiResponse<ATAMetricCO> execute(ATAMetricQry cmd) {\n        List<MetricDO> metricDOList = metricMapper.listBySubMetric(cmd.getOwnerId(), SubMetricType.ATA.getMetricSubTypeCode());\n        List<ATAMetricCO> ataMetricCOList = new ArrayList<>();\n        metricDOList.forEach(metricDO -> {\n            ATAMetricCO ataMetricCO = JSON.parseObject(metricDO.getMetricItem(), ATAMetricCO.class);\n            ataMetricCO.setOwnerId(metricDO.getUserId());\n            ataMetricCOList.add(ataMetricCO);\n        });\n        return MultiResponse.of(ataMetricCOList);\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/query/UserProfileGetQryExe.java",
    "content": "package com.alibaba.craftsman.command.query;\n\nimport com.alibaba.cola.dto.SingleResponse;\nimport com.alibaba.craftsman.dto.UserProfileGetQry;\nimport com.alibaba.craftsman.dto.clientobject.UserProfileCO;\nimport com.alibaba.craftsman.gatewayimpl.database.UserProfileMapper;\nimport com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n@Component\npublic class UserProfileGetQryExe {\n\n    @Resource\n    private UserProfileMapper userProfileMapper;\n\n    public SingleResponse<UserProfileCO> execute(UserProfileGetQry qry) {\n        UserProfileDO userProfileDO = userProfileMapper.getByUserId(qry.getUserId());\n        UserProfileCO userProfileCO = new UserProfileCO();\n        BeanUtils.copyProperties(userProfileDO, userProfileCO);\n        return SingleResponse.of(userProfileCO);\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/query/UserProfileListQryExe.java",
    "content": "package com.alibaba.craftsman.command.query;\n\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.craftsman.dto.UserProfileListQry;\nimport com.alibaba.craftsman.dto.clientobject.UserProfileCO;\nimport com.alibaba.craftsman.gatewayimpl.database.UserProfileMapper;\nimport com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Component\npublic class UserProfileListQryExe{\n\n    @Resource\n    private UserProfileMapper userProfileMapper;\n\n    public MultiResponse<UserProfileCO> execute(UserProfileListQry qry) {\n        List<UserProfileDO> userProfileDOList = userProfileMapper.listByDep(qry.getDep());\n        List<UserProfileCO> userProfileCOList = new ArrayList<>();\n        userProfileDOList.forEach(userDO -> {\n            UserProfileCO userProfileCO = new UserProfileCO();\n            BeanUtils.copyProperties(userDO, userProfileCO);\n            userProfileCOList.add(userProfileCO);\n        });\n        return MultiResponse.of(userProfileCOList);\n    }\n\n}\n\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/command/query/package-info.java",
    "content": "/**\n * This package contains QueryExecutors which are used to process Query Request.\n * \n * @author fulan.zjf\n */\npackage com.alibaba.craftsman.command.query;"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/event/handler/MetricItemCreatedHandler.java",
    "content": "package com.alibaba.craftsman.event.handler;\n\n\nimport com.alibaba.cola.catchlog.CatchAndLog;\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.api.UserProfileServiceI;\nimport com.alibaba.craftsman.dto.RefreshScoreCmd;\nimport com.alibaba.craftsman.dto.domainevent.MetricItemCreatedEvent;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n@CatchAndLog\npublic class MetricItemCreatedHandler {\n\n    @Autowired\n    private UserProfileServiceI userProfileService;\n\n    public Response execute(MetricItemCreatedEvent event) {\n        RefreshScoreCmd cmd = new RefreshScoreCmd(event.getUserId());\n        userProfileService.refreshScore(cmd);\n        return Response.buildSuccess();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/service/MetricsServiceImpl.java",
    "content": "package com.alibaba.craftsman.service;\n\nimport com.alibaba.cola.catchlog.CatchAndLog;\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.api.MetricsServiceI;\nimport com.alibaba.craftsman.command.*;\nimport com.alibaba.craftsman.command.query.ATAMetricQryExe;\nimport com.alibaba.craftsman.dto.*;\nimport com.alibaba.craftsman.dto.clientobject.ATAMetricCO;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\n\n/**\n * MetricsServiceImpl\n *\n * @author Frank Zhang\n * @date 2019-03-01 11:41 AM\n */\n@Service\n@CatchAndLog\npublic class MetricsServiceImpl implements MetricsServiceI{\n\n    @Resource\n    private ATAMetricAddCmdExe ataMetricAddCmdExe;\n    @Resource\n    private SharingMetricAddCmdExe sharingMetricAddCmdExe;\n    @Resource\n    private PatentMetricAddCmdExe patentMetricAddCmdExe;\n    @Resource\n    private PaperMetricAddCmdExe paperMetricAddCmdExe;\n    @Resource\n    private RefactoringMetricAddCmdExe refactoringMetricAddCmdExe;\n    @Resource\n    private MiscMetricAddCmdExe miscMetricAddCmdExe;\n    @Resource\n    private CodeReviewMetricAddCmdExe codeReviewMetricAddCmdExe;\n    @Resource\n    private MetricDeleteCmdExe metricDeleteCmdExe;\n    @Resource\n    private ATAMetricQryExe ataMetricQryExe;\n\n\n    @Override\n    public Response addATAMetric(ATAMetricAddCmd cmd) {\n        return ataMetricAddCmdExe.execute(cmd);\n    }\n\n    @Override\n    public Response addSharingMetric(SharingMetricAddCmd cmd) {\n        return sharingMetricAddCmdExe.execute(cmd);\n    }\n\n    @Override\n    public Response addPatentMetric(PatentMetricAddCmd cmd) {\n        return  patentMetricAddCmdExe.execute(cmd);\n    }\n\n    @Override\n    public Response addPaperMetric(PaperMetricAddCmd cmd) {\n        return  paperMetricAddCmdExe.execute(cmd);\n    }\n\n    @Override\n    public Response addRefactoringMetric(RefactoringMetricAddCmd cmd) {\n        return  refactoringMetricAddCmdExe.execute(cmd);\n    }\n\n    @Override\n    public Response addMiscMetric(MiscMetricAddCmd cmd) {\n        return  miscMetricAddCmdExe.execute(cmd);\n    }\n\n    @Override\n    public Response addCodeReviewMetric(CodeReviewMetricAddCmd cmd) {\n        return codeReviewMetricAddCmdExe.execute(cmd);\n    }\n\n    @Override\n    public Response deleteMetric(MetricDeleteCmd cmd) {\n        return metricDeleteCmdExe.execute(cmd);\n    }\n\n    @Override\n    public MultiResponse<ATAMetricCO> listATAMetrics(ATAMetricQry ataMetricQry) {\n        return ataMetricQryExe.execute(ataMetricQry);\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/service/UserProfileServiceImpl.java",
    "content": "package com.alibaba.craftsman.service;\n\nimport com.alibaba.cola.catchlog.CatchAndLog;\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.dto.SingleResponse;\nimport com.alibaba.craftsman.api.UserProfileServiceI;\nimport com.alibaba.craftsman.command.RefreshScoreCmdExe;\nimport com.alibaba.craftsman.command.UserProfileAddCmdExe;\nimport com.alibaba.craftsman.command.UserProfileUpdateCmdExe;\nimport com.alibaba.craftsman.command.query.UserProfileGetQryExe;\nimport com.alibaba.craftsman.command.query.UserProfileListQryExe;\nimport com.alibaba.craftsman.dto.*;\nimport com.alibaba.craftsman.dto.clientobject.UserProfileCO;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\n\n/**\n * UserProfileServiceImpl\n *\n * @author Frank Zhang\n * @date 2019-02-28 6:22 PM\n */\n@Service\n@CatchAndLog\npublic class UserProfileServiceImpl implements UserProfileServiceI{\n    @Resource\n    private UserProfileAddCmdExe userProfileAddCmdExe;\n    @Resource\n    private UserProfileUpdateCmdExe userProfileUpdateCmdExe;\n    @Resource\n    private RefreshScoreCmdExe refreshScoreCmdExe;\n    @Resource\n    private UserProfileGetQryExe userProfileGetQryExe;\n    @Resource\n    private UserProfileListQryExe userProfileListQryExe;\n\n\n    @Override\n    public Response addUserProfile(UserProfileAddCmd userProfileAddCmd) {\n        return  userProfileAddCmdExe.execute(userProfileAddCmd);\n    }\n\n    @Override\n    public Response updateUserProfile(UserProfileUpdateCmd cmd) {\n        return userProfileUpdateCmdExe.execute(cmd);\n    }\n\n    @Override\n    public Response refreshScore(RefreshScoreCmd cmd) {\n        return refreshScoreCmdExe.execute(cmd);\n    }\n\n    @Override\n    public SingleResponse<UserProfileCO> getUserProfileBy(UserProfileGetQry qry) {\n        return userProfileGetQryExe.execute(qry);\n    }\n\n    @Override\n    public MultiResponse<UserProfileCO> listUserProfileBy(UserProfileListQry qry) {\n        return userProfileListQryExe.execute(qry);\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/main/java/com/alibaba/craftsman/service/package-info.java",
    "content": "/**\n * Service is the facade of application API\n * \n * @author fulan.zjf\n */\npackage com.alibaba.craftsman.service;"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/test/java/com/alibaba/craftsman/app/ContextInterceptorTest.java",
    "content": "package com.alibaba.craftsman.app;\n\nimport com.alibaba.craftsman.dto.UserProfileAddCmd;\nimport com.alibaba.craftsman.dto.clientobject.UserProfileCO;\nimport org.junit.Test;\n\n/**\n * ContextInterceptorTest 单元测试\n *\n * @author Frank Zhang\n * @date 2019-03-01 9:38 AM\n */\npublic class ContextInterceptorTest {\n\n    @Test\n    public void testNoOperatorContext(){\n        UserProfileAddCmd userProfileAddCmd = new UserProfileAddCmd();\n        userProfileAddCmd.setUserProfileCO(new UserProfileCO());\n\n//        ContextInterceptor contextInterceptor = new ContextInterceptor();\n//        contextInterceptor.preIntercept(userProfileAddCmd);\n    }\n\n    @Test\n    public void testOperatorContext(){\n        UserProfileAddCmd userProfileAddCmd = new UserProfileAddCmd();\n        userProfileAddCmd.setUserProfileCO(new UserProfileCO());\n        userProfileAddCmd.setOperater(\"Frank\");\n\n//        ContextInterceptor contextInterceptor = new ContextInterceptor();\n//        contextInterceptor.preIntercept(userProfileAddCmd);\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-app/src/test/resources/logback-test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <!-- https://github.com/spring-projects/spring-boot/blob/v1.4.2.RELEASE/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\" />\n    </root>\n\n    <!--应用日志-->\n    <logger name=\"com.alibaba.craftsman\" level=\"DEBUG\"/>\n\n</configuration>"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>com.alibaba.craftsman</groupId>\n        <artifactId>craftsman.all</artifactId>\n        <version>1.0.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>craftsman-client</artifactId>\n    <packaging>jar</packaging>\n    <name>craftsman-client</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-dto</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>javax.validation</groupId>\n            <artifactId>validation-api</artifactId>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/api/MetricsServiceI.java",
    "content": "package com.alibaba.craftsman.api;\n\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.craftsman.dto.*;\nimport com.alibaba.craftsman.dto.clientobject.ATAMetricCO;\n\n/**\n * MetricsServiceI\n *\n * @author Frank Zhang\n * @date 2019-03-01 10:06 AM\n */\npublic interface MetricsServiceI {\n    Response addATAMetric(ATAMetricAddCmd cmd);\n    Response addSharingMetric(SharingMetricAddCmd cmd);\n    Response addPatentMetric(PatentMetricAddCmd cmd);\n    Response addPaperMetric(PaperMetricAddCmd cmd);\n    Response addRefactoringMetric(RefactoringMetricAddCmd cmd);\n    Response addMiscMetric(MiscMetricAddCmd cmd);\n    Response addCodeReviewMetric(CodeReviewMetricAddCmd cmd);\n    Response deleteMetric(MetricDeleteCmd cmd);\n    MultiResponse<ATAMetricCO> listATAMetrics(ATAMetricQry ataMetricQry);\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/api/UserProfileServiceI.java",
    "content": "package com.alibaba.craftsman.api;\n\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.dto.SingleResponse;\nimport com.alibaba.craftsman.dto.*;\nimport com.alibaba.craftsman.dto.clientobject.UserProfileCO;\n\n\n/**\n * UserProfileServiceI\n *\n * @author Frank Zhang\n * @date 2019-02-28 6:15 PM\n */\npublic interface UserProfileServiceI {\n    Response addUserProfile(UserProfileAddCmd cmd);\n    Response updateUserProfile(UserProfileUpdateCmd cmd);\n    Response refreshScore(RefreshScoreCmd cmd);\n    SingleResponse<UserProfileCO> getUserProfileBy(UserProfileGetQry qry);\n    MultiResponse<UserProfileCO>  listUserProfileBy(UserProfileListQry qry);\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/context/UserContext.java",
    "content": "package com.alibaba.craftsman.context;\n\nimport lombok.Data;\n\n/**\n * UserContext\n *\n * @author Frank Zhang\n * @date 2019-02-28 7:08 PM\n */\n@Data\npublic class UserContext {\n    private String operator;\n    private String loginUserId;\n    private String loginUserName;\n    private String loginUserRole;\n    private String loginUserPrivilege;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/ATAMetricAddCmd.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport com.alibaba.craftsman.dto.clientobject.ATAMetricCO;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\n\n/**\n * ATAMetricAddCmd\n *\n * @author Frank Zhang\n * @date 2019-03-01 10:12 AM\n */\n@Data\npublic class ATAMetricAddCmd extends CommonCommand{\n    @NotNull\n    private ATAMetricCO ataMetricCO;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/ATAMetricQry.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport lombok.Data;\n\n@Data\npublic class ATAMetricQry extends CommonCommand {\n    public String ownerId;\n}\n\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/CodeReviewMetricAddCmd.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.Positive;\n\n/**\n * CodeReviewMetricAddCmd\n *\n * @author Frank Zhang\n * @date 2019-03-01 10:09 AM\n */\n@Data\npublic class CodeReviewMetricAddCmd extends CommonCommand{\n\n    @NotEmpty\n    private String ownerId;\n\n    @NotEmpty\n    private String reviewId;\n\n    /**\n     * 评论数\n     */\n    @Positive\n    private int noteCount;\n\n    /**\n     * 文档链接\n     */\n    private String reviewDocLink;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/CommonCommand.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport com.alibaba.cola.dto.Command;\nimport lombok.Data;\n\n/**\n * 整个应用通用的Command\n *\n * @author Frank Zhang\n * @date 2019-02-28 7:18 PM\n */\npublic class CommonCommand extends Command{\n    private String operater;\n    private boolean needsOperator;\n\n    public String getOperater() {\n        return this.operater;\n    }\n\n    public void setOperater(String operater) {\n        this.operater = operater;\n        needsOperator = true;\n    }\n\n    public boolean isNeedsOperator(){\n        return needsOperator;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/MetricDeleteCmd.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport lombok.Data;\n\n/**\n * MetricDeleteCmd\n *\n * @author Frank Zhang\n * @date 2019-03-01 10:11 AM\n */\n@Data\npublic class MetricDeleteCmd extends CommonCommand{\n    /**\n     * Metric ID\n     */\n    private String metricId;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/MiscMetricAddCmd.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport com.alibaba.craftsman.dto.clientobject.MiscMetricCO;\nimport com.alibaba.craftsman.dto.clientobject.PatentMetricCO;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * MiscMetricAddCmd\n *\n * @author Frank Zhang\n * @date 2019-03-04 11:04 AM\n */\n@Data\npublic class MiscMetricAddCmd extends CommonCommand{\n    @NotNull\n    private MiscMetricCO miscMetricCO;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/PaperMetricAddCmd.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport com.alibaba.craftsman.dto.clientobject.PaperMetricCO;\nimport com.alibaba.craftsman.dto.clientobject.PatentMetricCO;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * PaperMetricAddCmd\n *\n * @author Frank Zhang\n * @date 2019-03-03 11:38 AM\n */\n@Data\npublic class PaperMetricAddCmd extends CommonCommand{\n    @NotNull\n    private PaperMetricCO paperMetricCO;\n}"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/PatentMetricAddCmd.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport com.alibaba.craftsman.dto.clientobject.ATAMetricCO;\nimport com.alibaba.craftsman.dto.clientobject.PatentMetricCO;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * PatentMetricAddCmd\n *\n * @author Frank Zhang\n * @date 2019-03-03 11:37 AM\n */\n@Data\npublic class PatentMetricAddCmd extends CommonCommand{\n    @NotNull\n    private PatentMetricCO  patentMetricCO;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/RefactoringMetricAddCmd.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport com.alibaba.craftsman.dto.clientobject.MiscMetricCO;\nimport com.alibaba.craftsman.dto.clientobject.RefactoringMetricCO;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * RefactoringMetricAddCmd\n *\n * @author Frank Zhang\n * @date 2019-03-04 11:04 AM\n */\n@Data\npublic class RefactoringMetricAddCmd extends CommonCommand{\n    @NotNull\n    private RefactoringMetricCO refactoringMetricCO;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/RefreshScoreCmd.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\n@Data\n@AllArgsConstructor\npublic class RefreshScoreCmd extends CommonCommand{\n    private String userId;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/SharingMetricAddCmd.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport com.alibaba.craftsman.dto.clientobject.SharingMetricCO;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * SharingMetricAddCmd\n *\n * @author Frank Zhang\n * @date 2019-03-01 10:12 AM\n */\n@Data\npublic class SharingMetricAddCmd extends CommonCommand{\n    @NotNull\n    private SharingMetricCO sharingMetricCO;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/UserProfileAddCmd.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport com.alibaba.craftsman.dto.clientobject.UserProfileCO;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * UserProfileAddCmd\n *\n * @author Frank Zhang\n * @date 2019-02-28 6:20 PM\n */\n@Data\npublic class UserProfileAddCmd extends CommonCommand {\n\n    @NotNull\n    private UserProfileCO userProfileCO;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/UserProfileGetQry.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport lombok.Data;\n\n@Data\npublic class UserProfileGetQry extends CommonCommand {\n    private String userId;\n    private String id;\n\n    public UserProfileGetQry(){\n\n    }\n\n}"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/UserProfileListQry.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport lombok.Data;\n\n@Data\npublic class UserProfileListQry extends CommonCommand {\n    private String dep;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/UserProfileUpdateCmd.java",
    "content": "package com.alibaba.craftsman.dto;\n\nimport com.alibaba.craftsman.dto.clientobject.UserProfileCO;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\n\n@Data\npublic class UserProfileUpdateCmd extends CommonCommand {\n\n    @NotNull\n    private UserProfileCO userProfileCO;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/clientobject/ATAMetricCO.java",
    "content": "package com.alibaba.craftsman.dto.clientobject;\n\nimport com.alibaba.cola.dto.ClientObject;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * ATAMetricCO\n *\n * @author Frank Zhang\n * @date 2019-03-01 5:39 PM\n */\n@Data\npublic class ATAMetricCO extends AbstractMetricCO {\n    @NotEmpty\n    private String title;//文章标题\n    private String url;//文章链接\n    private long thumbsUpCount;//点赞数\n    private long hitCount;//点击数\n    private long commentCount;//评论数\n    private long favoriteCount;//收藏数\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/clientobject/AbstractMetricCO.java",
    "content": "package com.alibaba.craftsman.dto.clientobject;\n\nimport com.alibaba.cola.dto.ClientObject;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * AbstractMetricCO\n *\n * @author Frank Zhang\n * @date 2019-03-04 11:32 AM\n */\n@Data\npublic abstract class AbstractMetricCO extends ClientObject{\n    /**\n     * The ownerId of this Metric Item\n     */\n    @NotEmpty\n    private String ownerId;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/clientobject/MiscMetricCO.java",
    "content": "package com.alibaba.craftsman.dto.clientobject;\n\nimport com.alibaba.cola.dto.ClientObject;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * MiscMetricCO\n *\n * @author Frank Zhang\n * @date 2019-03-04 10:58 AM\n */\n@Data\npublic class MiscMetricCO extends AbstractMetricCO {\n\n    /**\n     * 名称\n     */\n    @NotEmpty\n    private String name;\n\n    /**\n     * 内容\n     */\n    @NotEmpty\n    private String content;\n\n    /**\n     * 文档链接\n     */\n    private String docUrl;\n\n    /**\n     * 代码链接\n     */\n    private String codeUrl;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/clientobject/PaperMetricCO.java",
    "content": "package com.alibaba.craftsman.dto.clientobject;\n\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * PaperMetricCO\n *\n * @author Frank Zhang\n * @date 2019-03-03 11:16 AM\n */\n@Data\npublic class PaperMetricCO extends AbstractMetricCO{\n    @NotEmpty\n    private String paperName;\n    private String paperDesc;\n    private String magazine;\n    private String paperLink;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/clientobject/PatentMetricCO.java",
    "content": "package com.alibaba.craftsman.dto.clientobject;\n\nimport com.alibaba.cola.dto.ClientObject;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n/**\n * PatentMetricCO\n *\n * @author Frank Zhang\n * @date 2019-03-03 11:16 AM\n */\n@Data\npublic class PatentMetricCO  extends AbstractMetricCO {\n    public static final String FIRST_AUTHOR_TYPE = \"FIRST_AUTHOR\";\n    public static final String OTHER_AUTHOR_TYPE = \"OTHER_AUTHOR\";\n\n    @NotEmpty\n    private String patentName;\n    private String patentDesc;\n    private String patentNo;\n    private String patentUrl;\n    private String authorType;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/clientobject/RefactoringMetricCO.java",
    "content": "package com.alibaba.craftsman.dto.clientobject;\n\nimport com.alibaba.cola.dto.ClientObject;\nimport lombok.Data;\n\n/**\n * RefactoringMetricCO\n *\n * @author Frank Zhang\n * @date 2019-03-04 10:58 AM\n */\n@Data\npublic class RefactoringMetricCO  extends AbstractMetricCO {\n\n    public static final String METHOD_LEVEL = \"METHOD\";\n    public static final String MODULE_LEVEL = \"MODULE\";\n    public static final String PROJECT_LEVEL = \"PROJECT\";\n\n    /**\n     * 名称\n     */\n    private String name;\n\n    /**\n     * 内容\n     */\n    private String content;\n\n    /**\n     * 文档链接\n     */\n    private String docUrl;\n\n    /**\n     * 代码链接\n     */\n    private String codeUrl;\n    /**\n     * 重构的范围\n     */\n    private String refactoringLevel;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/clientobject/SharingMetricCO.java",
    "content": "package com.alibaba.craftsman.dto.clientobject;\n\nimport com.alibaba.cola.dto.ClientObject;\nimport lombok.Data;\n\n/**\n * SharingMetricCO\n *\n * @author Frank Zhang\n * @date 2019-03-02 4:55 PM\n */\n@Data\npublic class SharingMetricCO extends AbstractMetricCO {\n\n    public final static String TEAM_SCOPE = \"TEAM\";\n    public final static String BU_SCOPE = \"BU\";\n    public final static String ALIBABA_SCOPE = \"ALIBABA\";\n    public final static String COMMUNITY_SCOPE = \"COMMUNITY\";\n\n    /**\n     * 分享标题\n     */\n    private String sharingName;\n\n    /**\n     * 分享范围\n     */\n    private String sharingScope;\n\n    /**\n     * 分享日期\n     */\n    private String sharingDate;\n\n    /**\n     * 分享文档链接\n     */\n    private String sharingLink;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/clientobject/UserProfileCO.java",
    "content": "package com.alibaba.craftsman.dto.clientobject;\n\nimport com.alibaba.cola.dto.ClientObject;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\n\n@Data\npublic class UserProfileCO extends ClientObject {\n\n    public final static String IS_MANAGER = \"y\";\n    public final static String IS_NOT_MANAGER = \"n\";\n\n    public final static String DEV_ROLE = \"DEV\";\n    public final static String QA_ROLE = \"QA\";\n    public final static String OTHER_ROLE = \"OTHER\";\n\n    @NotEmpty\n    private String userId;\n    private String userName;\n    private String dep;\n    private String role;\n    private String isManager;\n\n    /**\n     * 综合得分\n     */\n    private double totalScore;\n\n    /**\n     * 代码质量分\n     */\n    private double appQualityScore;\n\n    /**\n     * 技术影响力分\n     */\n    private double techInfluenceScore;\n\n    /**\n     * 技术贡献分\n     */\n    private double techContributionScore;\n\n    /**\n     * 开发质量分\n     */\n    private double devQualityScore;\n\n    /**\n     * checkin代码量\n     */\n    private double checkinCodeQuantity;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/domainevent/CustomerCreatedEvent.java",
    "content": "package com.alibaba.craftsman.dto.domainevent;\n\n/**\n * CustomerCreatedEvent\n *\n * @author Frank Zhang\n * @date 2019-01-04 10:32 AM\n */\npublic class CustomerCreatedEvent {\n\n    private String customerId;\n\n    public String getCustomerId() {\n        return customerId;\n    }\n\n    public void setCustomerId(String customerId) {\n        this.customerId = customerId;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-client/src/main/java/com/alibaba/craftsman/dto/domainevent/MetricItemCreatedEvent.java",
    "content": "package com.alibaba.craftsman.dto.domainevent;\n\nimport lombok.Data;\n\n@Data\npublic class MetricItemCreatedEvent {\n\n    private String id;\n\n    private String userId;\n\n    private String mainMetricType;\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>com.alibaba.craftsman</groupId>\n        <artifactId>craftsman.all</artifactId>\n        <version>1.0.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>craftsman-domain</artifactId>\n    <packaging>jar</packaging>\n    <name>craftsman-domain</name>\n\n    <dependencies>\n        <!-- COLA Framework -->\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-dto</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-exception</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cola</groupId>\n            <artifactId>cola-component-domain-starter</artifactId>\n        </dependency>\n        <!-- COLA Framework End-->\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/DomainFactory.java",
    "content": "package com.alibaba.craftsman.domain;\n\nimport com.alibaba.craftsman.domain.user.UserProfile;\n\npublic class DomainFactory {\n\n    public static UserProfile getUserProfile(){\n        return new UserProfile();\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/gateway/MetricGateway.java",
    "content": "package com.alibaba.craftsman.domain.gateway;\n\nimport com.alibaba.craftsman.domain.metrics.MetricItem;\nimport com.alibaba.craftsman.domain.metrics.SubMetric;\nimport com.alibaba.craftsman.domain.metrics.appquality.AppMetric;\nimport com.alibaba.craftsman.domain.metrics.devquality.BugMetric;\n\nimport java.util.List;\n\n/**\n * MetricGateway\n *\n * @author Frank Zhang\n * @date 2020-07-02 12:16 PM\n */\npublic interface MetricGateway {\n    void save(MetricItem metricItem);\n    List<SubMetric> listByTechContribution(String userId);\n    List<SubMetric> listByTechInfluence(String userId);\n    BugMetric getBugMetric(String userId);\n    AppMetric getAppMetric(String userId);\n    void delete(String id, String operator);\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/gateway/UserProfileGateway.java",
    "content": "package com.alibaba.craftsman.domain.gateway;\n\nimport com.alibaba.craftsman.domain.user.UserProfile;\n\n/**\n * UserProfileGateway\n *\n * @author Frank Zhang\n * @date 2020-07-02 12:16 PM\n */\npublic interface UserProfileGateway {\n    void create(UserProfile userProfile);\n    void update(UserProfile userProfile);\n    UserProfile getByUserId(String userId);\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/JSONPropertyFilter.java",
    "content": "package com.alibaba.craftsman.domain.metrics;\n\nimport com.alibaba.fastjson.serializer.PropertyFilter;\n\n/**\n * JSONPropertyFilter\n *\n * @author Frank Zhang\n * @date 2019-03-02 10:53 PM\n */\npublic class JSONPropertyFilter implements PropertyFilter {\n\n    public static JSONPropertyFilter singleton = new JSONPropertyFilter();\n\n    @Override\n    public boolean apply(Object object, String name, Object value) {\n        if(name.equalsIgnoreCase(\"context\")){\n            return false;\n        }\n        if(name.equalsIgnoreCase(\"extValues\")){\n            return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/MainMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics;\n\nimport lombok.Data;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Main Metric, 一个主度量（MainMetric）是由多个子度量（SubMetric）组成的。\n *\n * @author Frank Zhang\n * @date 2018-08-28 2:00 PM\n */\n@Data\npublic abstract class MainMetric extends Metric{\n\n\n    protected MainMetricType metricMainType;\n\n    protected List<SubMetric> subMetrics = new ArrayList<>();\n\n    public MainMetric(){\n    }\n\n    public void addSubMetric(SubMetric metric){\n        subMetrics.add(metric);\n    }\n\n    @Override\n    public String getName() {\n        return metricMainType.getMetricName();\n    }\n\n    @Override\n    public String getCode(){\n        return metricMainType.getMetricCode();\n    }\n\n\n    @Override\n    public double calculateScore() {\n        double mainMetricScore = 0;\n        for (Metric subMetric : subMetrics) {\n            mainMetricScore = mainMetricScore + subMetric.calculateScore() * subMetric.getWeight();\n        }\n        return mainMetricScore;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/MainMetricType.java",
    "content": "package com.alibaba.craftsman.domain.metrics;\n\npublic enum MainMetricType {\n    APP_QUALITY(\"app-quality\",\"应用质量\"),\n    TECH_INFLUENCE(\"tech-influence\",\"技术影响力\"),\n    TECH_CONTRIBUTION(\"tech-contribution\",\"技术贡献\"),\n    DEV_QUALITY(\"dev-quality\",\"开发质量\");\n\n    private String metricCode;\n    private String metricName;\n\n    private MainMetricType(String metricCode, String metricName){\n        this.metricCode = metricCode;\n        this.metricName = metricName;\n    }\n\n    public String getMetricCode() {\n        return metricCode;\n    }\n\n    public String getMetricName() {\n        return metricName;\n    }\n\n    public static MainMetricType of(String metricCode){\n        if(metricCode == null){\n            return null;\n        }\n        for (MainMetricType metricMainType : MainMetricType.values()) {\n            if(metricCode.equals(metricMainType.metricCode)){\n                return metricMainType;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/Measurable.java",
    "content": "package com.alibaba.craftsman.domain.metrics;\n\nimport java.io.Serializable;\n\n/**\n * Measurable\n * 可度量的\n * @author Frank Zhang\n * @date 2018-07-04 1:32 PM\n */\npublic interface Measurable extends Serializable{\n\n    /**\n     * 计算分数\n     * @return\n     */\n    public double calculateScore();\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/Metric.java",
    "content": "package com.alibaba.craftsman.domain.metrics;\n\n\nimport com.alibaba.cola.domain.Entity;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * Metric\n * 指标\n * @author Frank Zhang\n * @date 2018-07-04 1:23 PM\n */\n@Entity\npublic abstract class Metric implements Measurable{\n\n    private double score;\n\n    @Getter\n    @Setter\n    protected UserProfile metricOwner;\n\n    public Metric(){\n\n    }\n\n    public Metric(UserProfile metricOwner){\n        this.metricOwner = metricOwner;\n    }\n\n\n    /**\n     * 度量名称，用于UI显示\n     * @return\n     */\n    abstract public String getName();\n\n    /**\n     * 度量Code，用于数据库存储\n     * @return\n     */\n    abstract public String getCode();\n\n    abstract public double getWeight();\n\n    @Override\n    public String toString(){\n        return this.getName() + \" \" + this.score;\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/MetricItem.java",
    "content": "package com.alibaba.craftsman.domain.metrics;\n\nimport com.alibaba.cola.domain.Entity;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.annotation.JSONField;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * MetricItem\n * 指标项，多个指标项可以构成一个指标\n * @author Frank Zhang\n * @date 2018-07-04 1:23 PM\n */\n@Data\n@Entity\n@Slf4j\npublic abstract class MetricItem implements Measurable{\n\n    /**\n     * The metric this MetricItem belongs to\n     */\n    @JSONField(serialize = false)\n    private SubMetric subMetric;\n\n    /**\n     * The owner of this MetricItem\n     */\n    @JSONField(serialize = false)\n    private UserProfile metricOwner;\n\n    public void setSubMetric(SubMetric subMetric){\n        this.subMetric = subMetric;\n        this.metricOwner = subMetric.getMetricOwner();\n    }\n    /**\n     * 将度量项的转成JSON\n     * @return\n     */\n    public String toJsonString() {\n        String jsonStr = JSON.toJSONString(this, JSONPropertyFilter.singleton);\n        log.debug(\"\\n From : \" + this + \" \\n To: \" + jsonStr);\n        return jsonStr;\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/SubMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics;\n\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport lombok.Data;\nimport lombok.Getter;\nimport lombok.Setter;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Sub Metric,  一个主度量（MainMetric）可以有一个或者多个子度量（SubMetric）组成的。\n *\n * @author Frank Zhang\n * @date 2018-08-27 6:44 PM\n */\n@Data\npublic abstract class SubMetric extends Metric {\n\n    protected SubMetricType subMetricType;\n\n    protected MainMetric parent;\n\n    @Getter\n    private List<MetricItem> metricItemList = new ArrayList<>();\n\n    public SubMetric(){\n\n    }\n\n    public void setParent(MainMetric parent){\n        this.parent = parent;\n        this.metricOwner = parent.metricOwner;\n        parent.addSubMetric(this);\n    }\n\n    /**\n     * 添加度量项\n     * @param metricItem\n     */\n    public void addMetricItem(MetricItem metricItem){\n        metricItemList.add(metricItem);\n    }\n\n\n    @Override\n    public String getName() {\n        return subMetricType.getMetricSubTypeName();\n    }\n\n    @Override\n    public String getCode(){\n        return subMetricType.getMetricSubTypeCode();\n    }\n\n    @Override\n    public double calculateScore() {\n        double subMetricScore = 0;\n        for (MetricItem metricItem : metricItemList) {\n            subMetricScore = subMetricScore + metricItem.calculateScore();\n        }\n        return subMetricScore;\n    }\n\n    @Override\n    public UserProfile getMetricOwner(){\n        return parent.getMetricOwner();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/SubMetricType.java",
    "content": "package com.alibaba.craftsman.domain.metrics;\n\n/**\n * SubMetricType\n *\n * @author Frank Zhang\n * @date 2018-08-27 4:54 PM\n */\npublic enum SubMetricType {\n\n    LongMethod(MainMetricType.APP_QUALITY, \"LongMethod\",\"超长方法\"),\n    Cyclomatic(MainMetricType.APP_QUALITY, \"Cyclomatic\",\"圈复杂度\"),\n    Duplication(MainMetricType.APP_QUALITY, \"Duplication\",\"代码重复度\"),\n\n    App(MainMetricType.APP_QUALITY, \"App\",\"App应用\"),\n\n    ATA(MainMetricType.TECH_INFLUENCE, \"ATA\", \"ATA文章\"),\n    Sharing(MainMetricType.TECH_INFLUENCE, \"Sharing\", \"技术分享\"),\n    Patent(MainMetricType.TECH_INFLUENCE, \"Patent\", \"专利\"),\n    Paper(MainMetricType.TECH_INFLUENCE, \"Paper\", \"论文\"),\n\n    CodeReview(MainMetricType.TECH_CONTRIBUTION, \"CodeReview\", \"Code Review\"),\n    Refactoring(MainMetricType.TECH_CONTRIBUTION, \"Refactoring\", \"重构\"),\n    Misc(MainMetricType.TECH_CONTRIBUTION, \"Misc\", \"其他贡献\"),\n\n    Bug(MainMetricType.DEV_QUALITY, \"Bug\", \"提测Bug\"),\n    Fault(MainMetricType.DEV_QUALITY, \"Fault\", \"故障\"),\n    ;\n\n    //度量类型\n    private MainMetricType parentType;\n\n    //度量项Code\n    private String metricSubTypeCode;\n\n    //度量项中文名称\n    private String metricSubTypeName;\n\n    private SubMetricType(MainMetricType parentType, String metricSubTypeCode, String metricSubTypeName){\n        this.parentType = parentType;\n        this.metricSubTypeCode = metricSubTypeCode;\n        this.metricSubTypeName = metricSubTypeName;\n    }\n\n    public MainMetricType getParentType() {\n        return parentType;\n    }\n\n    public String getMetricSubTypeCode() {\n        return metricSubTypeCode;\n    }\n\n    public String getMetricSubTypeName() {\n        return metricSubTypeName;\n    }\n\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/appquality/AppMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.appquality;\n\nimport com.alibaba.craftsman.domain.metrics.SubMetric;\nimport com.alibaba.craftsman.domain.metrics.SubMetricType;\n\npublic class AppMetric extends SubMetric {\n\n    public AppMetric(){\n        this.subMetricType = SubMetricType.App;\n    }\n\n    @Override\n    public double getWeight() {\n        return metricOwner.getWeight().getUnanimousWeight();\n    }\n\n    @Override\n    public double calculateScore() {\n        int appCount = super.getMetricItemList().size();\n        if (appCount == 0){\n            return 0;\n        }\n        double sumScore = super.calculateScore();\n        return sumScore/appCount;\n    }\n}"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/appquality/AppMetricItem.java",
    "content": "package com.alibaba.craftsman.domain.metrics.appquality;\n\nimport com.alibaba.craftsman.domain.metrics.MetricItem;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\n\n@Data\n@Slf4j\npublic class AppMetricItem extends MetricItem {\n\n    private String appName;//应用名称\n    private int cyclomaticComplexityCount;//圈复杂度超标的数目\n    private int duplicatedMethodCount;//重复代码的数目\n    private int longMethodCount;//长方法的数目\n    private int blockedCodeConductCount;//不符合编码标准的数目\n\n    private static final int FULL_SCORE=100;\n    private static final int STEP_SIZE=10;\n    private static final int STEP_MINUS_SCORE = 1;\n\n    @Override\n    public double calculateScore() {\n        double score = FULL_SCORE;\n        score = duductScore(score, cyclomaticComplexityCount);\n        score = duductScore(score, duplicatedMethodCount);\n        score = duductScore(score, longMethodCount);\n        score = duductScore(score, blockedCodeConductCount);\n        log.debug(\"Calculated App score is \"+score );\n        return score;\n    }\n\n    private double duductScore(double score, int count) {\n        for (int counter = STEP_SIZE; counter <= count; counter = counter + STEP_SIZE) {\n            score = score - STEP_MINUS_SCORE;\n        }\n        return score;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/appquality/AppQualityMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.appquality;\n\nimport com.alibaba.craftsman.domain.metrics.MainMetric;\nimport com.alibaba.craftsman.domain.metrics.MainMetricType;\nimport com.alibaba.craftsman.domain.metrics.devquality.BugMetric;\nimport com.alibaba.craftsman.domain.user.UserProfile;\n\npublic class AppQualityMetric extends MainMetric {\n\n    private AppMetric appMetric;\n\n    public AppQualityMetric(UserProfile metricOwner){\n        this.metricOwner = metricOwner;\n        metricOwner.setAppQualityMetric(this);\n        this.metricMainType = MainMetricType.APP_QUALITY;\n    }\n\n    @Override\n    public double getWeight() {\n        return metricOwner.getWeight().getAppQualityWeight();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/devquality/BugMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.devquality;\n\nimport com.alibaba.craftsman.domain.metrics.*;\nimport com.alibaba.craftsman.domain.user.Role;\n\n\n/**\n * BUG数指标\n */\npublic class BugMetric extends SubMetric {\n\n    public BugMetric(){\n        this.subMetricType = SubMetricType.Bug;\n    }\n\n    @Override\n    public double getWeight() {\n        return metricOwner.getWeight().getUnanimousWeight();\n    }\n\n    @Override\n    public double calculateScore() {\n        if(metricOwner.getRole() == Role.OTHER){\n            return 0;\n        }\n        return super.calculateScore();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/devquality/BugMetricItem.java",
    "content": "package com.alibaba.craftsman.domain.metrics.devquality;\n\nimport com.alibaba.craftsman.domain.metrics.MetricItem;\nimport lombok.Data;\n\n@Data\npublic class BugMetricItem extends MetricItem{\n\n    private int bugCount; //缺陷数量\n    private long checkInCodeCount; //check in的代码量\n\n    private static int DEFAULT_SCORE = 100;\n    private static double STEP_SIZE = 1;\n    private static double STEP_MINUS_SCORE = 5;\n\n    public BugMetricItem(int bugCount, long checkInCodeCount){\n        this.bugCount = bugCount;\n        this.checkInCodeCount = checkInCodeCount;\n    }\n\n    @Override\n    public double calculateScore() {\n        double score = DEFAULT_SCORE;\n        //千行代码缺陷数\n        double bugPerThousandLinesCode = bugCount * 1000 / checkInCodeCount;\n        for(double counter = STEP_SIZE; counter <= bugPerThousandLinesCode; counter = counter + STEP_SIZE){\n            score = score - STEP_MINUS_SCORE;\n        }\n        return score;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/devquality/DevQualityMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.devquality;\n\nimport com.alibaba.craftsman.domain.metrics.MainMetric;\nimport com.alibaba.craftsman.domain.metrics.MainMetricType;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport lombok.Data;\n\n@Data\npublic class DevQualityMetric extends MainMetric {\n\n    private BugMetric bugMetric;\n\n    public DevQualityMetric(UserProfile metricOwner){\n        this.metricOwner = metricOwner;\n        metricOwner.setDevQualityMetric(this);\n        this.metricMainType = MainMetricType.DEV_QUALITY;\n    }\n\n    @Override\n    public double getWeight() {\n        return metricOwner.getWeight().getDevQualityWeight();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techcontribution/CodeReviewMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techcontribution;\n\nimport com.alibaba.craftsman.domain.metrics.*;\n\n/**\n * CodeReview指标\n * @author xueliang.sxl, alisa.hsh, xiangning.lxn\n */\npublic class CodeReviewMetric extends SubMetric {\n\n    public CodeReviewMetric(){\n        this.subMetricType = SubMetricType.CodeReview;\n    }\n\n    public CodeReviewMetric(MainMetric parent) {\n        this.parent = parent;\n        parent.addSubMetric(this);\n        this.subMetricType = SubMetricType.CodeReview;\n    }\n\n    @Override\n    public double getWeight() {\n        return metricOwner.getWeight().getUnanimousWeight();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techcontribution/CodeReviewMetricItem.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techcontribution;\n\nimport com.alibaba.craftsman.domain.metrics.MetricItem;\nimport com.alibaba.fastjson.JSON;\nimport lombok.Data;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * CodeReview指标项\n * @author xueliang.sxl, alisa.hsh, xiangning.lxn\n */\n@Data\npublic class CodeReviewMetricItem extends MetricItem {\n\n    /**\n     * 评审id\n     */\n    private String reviewId;\n\n    /**\n     * 评论数量\n     */\n    private int noteCount;\n\n\n    /**\n     * 文档链接\n     */\n    private String reviewDocLink;\n\n    /**\n     * 每条评论0.1分\n     */\n    private static double CODE_REVIEW_SCORE = 0.1;\n\n    public CodeReviewMetricItem(){\n\n    }\n\n\n    public static CodeReviewMetricItem valueOf(String json){\n        return JSON.parseObject(json, CodeReviewMetricItem.class);\n    }\n\n    /**\n     * 计算当前度量项分数\n     * @return\n     */\n    @Override\n    public double calculateScore() {\n        return noteCount * CODE_REVIEW_SCORE;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techcontribution/ContributionMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techcontribution;\n\nimport com.alibaba.craftsman.domain.metrics.MainMetric;\nimport com.alibaba.craftsman.domain.metrics.MainMetricType;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport lombok.Data;\n\n/**\n * ContributionMetric\n *\n * @author Frank Zhang\n * @date 2018-08-27 7:06 PM\n */\n@Data\npublic class ContributionMetric extends MainMetric {\n\n    private CodeReviewMetric codeReviewMetric;\n    private RefactoringMetric refactoringMetric;\n    private MiscMetric miscMetric;\n\n    public ContributionMetric(UserProfile metricOwner){\n        this.metricOwner = metricOwner;\n        metricOwner.setContributionMetric(this);\n        this.metricMainType = MainMetricType.TECH_CONTRIBUTION;\n    }\n\n    @Override\n    public double getWeight() {\n        return metricOwner.getWeight().getTechContributionWeight();\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techcontribution/MiscMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techcontribution;\n\nimport com.alibaba.craftsman.domain.metrics.*;\n\n/**\n * Miscellaneous，其他度量，任何的技术亮点都可以添加\n * @author frankzhang\n */\npublic class MiscMetric extends SubMetric {\n\n    public MiscMetric(){\n        this.subMetricType = SubMetricType.Misc;\n    }\n\n    public MiscMetric(MainMetric parent) {\n        this.parent = parent;\n        parent.addSubMetric(this);\n        this.subMetricType = SubMetricType.Misc;\n    }\n\n    @Override\n    public double getWeight() {\n        return metricOwner.getWeight().getUnanimousWeight();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techcontribution/MiscMetricItem.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techcontribution;\n\nimport com.alibaba.craftsman.domain.metrics.MetricItem;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport lombok.Data;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 设计指标度量项\n * @author xueliang.sxl, alisa.hsh, xiangning.lxn\n */\n@Data\npublic class MiscMetricItem extends MetricItem {\n\n    /**\n     * 名称\n     */\n    private String name;\n\n    /**\n     * 内容\n     */\n    private String content;\n\n    /**\n     * 文档链接\n     */\n    private String docUrl;\n\n    /**\n     * 代码链接\n     */\n    private String codeUrl;\n\n    private static double OUTSTANDING_CONTRIBUTION_SCORE = 20;\n\n    public MiscMetricItem(){\n\n    }\n\n    public MiscMetricItem(String name, String content, String docUrl, String codeUrl){\n        this.name = name;\n        this.codeUrl = codeUrl;\n        this.content = content;\n        this.docUrl = docUrl;\n    }\n\n\n    public static MiscMetricItem valueOf(String json){\n        return JSON.parseObject(json, MiscMetricItem.class);\n    }\n\n    /**\n     * 计算当前度量项分数\n     * @return\n     */\n    @Override\n    public double calculateScore() {\n        return OUTSTANDING_CONTRIBUTION_SCORE;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techcontribution/RefactoringLevel.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techcontribution;\n\nimport lombok.Getter;\n\n/**\n * RefactoringLevel\n *\n * @author Frank Zhang\n * @date 2018-09-20 3:37 PM\n */\npublic enum RefactoringLevel {\n\n    METHOD(2, \"方法级别的重构\"),\n    MODULE( 4, \"模块级别的重构（多个方法和类的重构）\"),\n    PROJECT(10, \"项目级别的重构（超过3个人日的重构项目）\");\n\n\n    @Getter\n    private double score;\n\n    private String desc;\n\n    RefactoringLevel(double score, String desc) {\n        this.score = score;\n        this.desc = desc;\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techcontribution/RefactoringMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techcontribution;\n\nimport com.alibaba.craftsman.domain.metrics.*;\n\n/**\n * 重构指标\n * @author xueliang.sxl, alisa.hsh, xiangning.lxn\n */\npublic class RefactoringMetric extends SubMetric {\n\n    public RefactoringMetric(){\n        this.subMetricType = SubMetricType.Refactoring;\n    }\n\n    public RefactoringMetric(MainMetric parent) {\n        this.parent = parent;\n        parent.addSubMetric(this);\n        this.subMetricType = SubMetricType.Refactoring;\n    }\n\n    @Override\n    public double getWeight() {\n        return  metricOwner.getWeight().getUnanimousWeight();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techcontribution/RefactoringMetricItem.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techcontribution;\n\nimport com.alibaba.cola.exception.Assert;\nimport com.alibaba.cola.exception.BizException;\nimport com.alibaba.craftsman.domain.metrics.MetricItem;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.ATAMetricItem;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport lombok.Data;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 重构指标度量项\n * @author xueliang.sxl, alisa.hsh, xiangning.lxn\n */\n@Data\npublic class RefactoringMetricItem extends MetricItem {\n\n    /**\n     * 名称\n     */\n    private String name;\n\n    /**\n     * 内容\n     */\n    private String content;\n\n    /**\n     * 文档链接\n     */\n    private String docUrl;\n\n    /**\n     * 代码链接\n     */\n    private String codeUrl;\n    /**\n     * 重构的范围\n     */\n    private RefactoringLevel refactoringLevel;\n\n    public RefactoringMetricItem(){\n    }\n\n    public static RefactoringMetricItem valueOf(String json){\n        return JSON.parseObject(json, RefactoringMetricItem.class);\n    }\n\n    /**\n     * 计算当前度量项分数\n     * @return\n     */\n    @Override\n    public double calculateScore() {\n        Assert.notNull(refactoringLevel, \"Refactoring Level can not be null\");\n        return refactoringLevel.getScore();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techinfluence/ATAMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techinfluence;\n\nimport com.alibaba.craftsman.domain.metrics.*;\n\n/**\n * ATAMetric\n * ATA文章指标\n * @author Frank Zhang\n * @date 2018-07-04 1:24 PM\n */\npublic class ATAMetric extends SubMetric {\n\n    public ATAMetric(){\n        this.subMetricType = SubMetricType.ATA;\n    }\n\n    public ATAMetric(MainMetric parent) {\n        this.parent = parent;\n        parent.addSubMetric(this);\n        this.subMetricType = SubMetricType.ATA;\n    }\n\n    @Override\n    public double getWeight() {\n        return  parent.getMetricOwner().getWeight().getUnanimousWeight();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techinfluence/ATAMetricItem.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techinfluence;\n\nimport com.alibaba.craftsman.domain.metrics.MetricItem;\nimport com.alibaba.fastjson.JSON;\nimport lombok.Data;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * ATAMetricItem\n * 线上分享，ATA文章指标项\n * @author Frank Zhang\n * @date 2018-07-04 3:20 PM\n */\n@Data\n@Slf4j\npublic class ATAMetricItem extends MetricItem {\n\n    private String authorId;//作者\n    private String title;//文章标题\n    private String url;//文章链接\n    private long thumbsUpCount;//点赞数\n    private long hitCount;//点击数\n    private long commentCount;//评论数\n    private long favoriteCount;//收藏数\n\n    private static int HIT_STEP_SIZE = 100;\n    private static int THUMB_UPS_STEP_SIZE = 20;\n    private static int FAVORITE_STEP_SIZE = 15;\n    private static int COMMENT_STEP_SIZE = 3;\n    private static double STEP_SCORE = 0.25;\n    private static double BASIC_SCORE = 0.5;\n\n\n    public ATAMetricItem(){\n\n    }\n\n    public ATAMetricItem(String title, long thumbsUpCount, long hitCount, long favoriteCount, long commentCount) {\n        this.title = title;\n        this.thumbsUpCount = thumbsUpCount;\n        this.hitCount = hitCount;\n        this.favoriteCount = favoriteCount;\n        this.commentCount = commentCount;\n    }\n\n    public static ATAMetricItem valueOf(String json){\n        return JSON.parseObject(json, ATAMetricItem.class);\n    }\n\n    @Override\n    public double calculateScore() {\n        log.debug(\"calculate score for : \" + this);\n        double score = BASIC_SCORE;\n        score = addScoreByHitCount(score);\n        score = addScoreByThumbsupCount(score);\n        score = addScoreByFavoriteCount(score);\n        score = addScoreByCommentCount(score);\n        log.debug(\"calculated score is : \" + score);\n        return score;\n    }\n\n    private double addScoreByHitCount(double score) {\n        for(int counter = HIT_STEP_SIZE; counter <= hitCount; counter = counter + HIT_STEP_SIZE){\n            score = score + STEP_SCORE;\n        }\n        return score;\n    }\n\n    private double addScoreByThumbsupCount(double score){\n        for(int counter = THUMB_UPS_STEP_SIZE; counter <= thumbsUpCount; counter = counter + THUMB_UPS_STEP_SIZE){\n            score = score + STEP_SCORE;\n        }\n        return score;\n    }\n\n    private double addScoreByFavoriteCount(double score){\n        for(int counter = FAVORITE_STEP_SIZE; counter <= favoriteCount; counter = counter + FAVORITE_STEP_SIZE){\n            score = score + STEP_SCORE;\n        }\n        return score;\n    }\n\n    private double addScoreByCommentCount(double score){\n        for(int counter = COMMENT_STEP_SIZE; counter <= commentCount; counter = counter + COMMENT_STEP_SIZE){\n            score = score + STEP_SCORE;\n        }\n        return score;\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techinfluence/AuthorType.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techinfluence;\n\nimport lombok.Getter;\n\n/**\n * 论文专利作者类型枚举\n *\n * @author Frank Zhang\n * @date 2018-09-20 3:28 PM\n */\npublic enum AuthorType {\n\n    FIRST_AUTHOR(AuthorType.FIRST_AUTHOR_PATENT_SCORE, \"专利或者论文的第一作者\"),\n    OTHER_AUTHOR(AuthorType.OTHER_AUTHOR_PATENT_SCORE, \"专利或论文的其他作者\");\n\n    private static final double FIRST_AUTHOR_PATENT_SCORE = 20;\n    private static final double OTHER_AUTHOR_PATENT_SCORE = 5;\n\n    @Getter\n    private double score;\n    private String desc;\n\n    private AuthorType(double score, String desc) {\n        this.score = score;\n        this.desc = desc;\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techinfluence/InfluenceMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techinfluence;\n\nimport com.alibaba.craftsman.domain.metrics.MainMetric;\nimport com.alibaba.craftsman.domain.metrics.MainMetricType;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport lombok.Data;\n\n/**\n * InfluenceMetric\n * 影响力指标\n * @author Frank Zhang\n * @date 2018-07-04 1:24 PM\n */\n@Data\npublic class InfluenceMetric extends MainMetric {\n    private ATAMetric ataMetric;\n    private PatentMetric patentMetric;\n    private SharingMetric sharingMetric;\n    private PaperMetric paperMetric;\n\n    public InfluenceMetric(UserProfile metricOwner){\n        this.metricOwner = metricOwner;\n        metricOwner.setInfluenceMetric(this);\n        this.metricMainType = MainMetricType.TECH_INFLUENCE;\n    }\n\n    @Override\n    public double getWeight() {\n        return  metricOwner.getWeight().getTechInfluenceWeight();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techinfluence/PaperMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techinfluence;\n\nimport com.alibaba.craftsman.domain.metrics.*;\n\n/**\n * 论文子度量\n * PaperMetric\n *\n * @author Frank Zhang\n * @date 2018-09-20 3:26 PM\n */\npublic class PaperMetric extends SubMetric {\n\n\tpublic PaperMetric(){\n\t\tthis.subMetricType = SubMetricType.Paper;\n\t}\n\n\tpublic PaperMetric(MainMetric parent) {\n\t\tthis.parent = parent;\n\t\tparent.addSubMetric(this);\n\t\tthis.subMetricType = SubMetricType.Paper;\n\t}\n\n\t@Override\n\tpublic double getWeight() {\n\t\treturn parent.getMetricOwner().getWeight().getUnanimousWeight();\n\t}\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techinfluence/PaperMetricItem.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techinfluence;\n\nimport com.alibaba.craftsman.domain.metrics.MetricItem;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport lombok.Data;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * PaperMetricItem\n *\n * @author Frank Zhang\n * @date 2018-09-20 3:26 PM\n */\n@Data\npublic class PaperMetricItem extends MetricItem {\n\n\tprivate String paperName;\n\tprivate String paperDesc;\n\tprivate String magazine;\n\tprivate String paperLink;\n\n\tprivate static final double PAPER_SCORE = 10;\n\n\tpublic PaperMetricItem(){\n\n\t}\n\n\tpublic PaperMetricItem(String paperName, String paperDesc, String magazine, String paperLink) {\n\t\tthis.paperName = paperName;\n\t\tthis.paperDesc = paperDesc;\n\t\tthis.magazine = magazine;\n\t\tthis.paperLink = paperLink;\n\t}\n\n\tpublic static PaperMetricItem valueOf(String json){\n\t\treturn JSON.parseObject(json, PaperMetricItem.class);\n\t}\n\n\t@Override\n\tpublic double calculateScore() {\n\t\treturn PAPER_SCORE;\n\t}\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techinfluence/PatentMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techinfluence;\n\nimport com.alibaba.craftsman.domain.metrics.*;\n\n/**\n * 技术专利指标\n * @author xueliang.sxl\n */\npublic class PatentMetric extends SubMetric {\n\n    public PatentMetric(){\n        this.subMetricType = SubMetricType.Patent;\n    }\n\n    public PatentMetric(MainMetric parent) {\n        this.parent = parent;\n        parent.addSubMetric(this);\n        this.subMetricType = SubMetricType.Patent;\n    }\n\n    @Override\n    public double getWeight() {\n        return parent.getMetricOwner().getWeight().getUnanimousWeight();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techinfluence/PatentMetricItem.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techinfluence;\n\nimport com.alibaba.craftsman.domain.metrics.MetricItem;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport lombok.Data;\n\n/**\n * 技术创新指标项\n * @author xueliang.sxl\n */\n@Data\npublic class PatentMetricItem extends MetricItem {\n\n    private String patentName;\n    private String patentDesc;\n    private String patentNo;\n    private String patentUrl;\n    private AuthorType authorType;\n\n    public PatentMetricItem(){\n\n    }\n\n    public PatentMetricItem(String patentName, String patentDesc, String patentNo, String patentUrl, AuthorType authorType){\n        this.patentName = patentName;\n        this.patentDesc = patentDesc;\n        this.patentNo = patentNo;\n        this.patentUrl = patentUrl;\n        this.authorType = authorType;\n    }\n\n    public static PatentMetricItem valueOf(String json){\n        return JSON.parseObject(json, PatentMetricItem.class);\n    }\n\n    @Override\n    public double calculateScore() {\n       return authorType.getScore();\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techinfluence/SharingMetric.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techinfluence;\n\nimport com.alibaba.craftsman.domain.metrics.*;\n\n/**\n * SharingMetric\n * 线下技术分享指标\n * @author Frank Zhang\n * @date 2018-07-04 1:25 PM\n */\npublic class SharingMetric extends SubMetric {\n\n    public SharingMetric(){\n        this.subMetricType = SubMetricType.Sharing;\n    }\n\n    public SharingMetric(MainMetric parent) {\n        this.parent = parent;\n        parent.addSubMetric(this);\n        this.subMetricType = SubMetricType.Sharing;\n    }\n\n    @Override\n    public double getWeight() {\n        return  parent.getMetricOwner().getWeight().getUnanimousWeight();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techinfluence/SharingMetricItem.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techinfluence;\n\nimport com.alibaba.craftsman.domain.metrics.MetricItem;\nimport com.alibaba.fastjson.JSON;\nimport lombok.Data;\n\nimport java.util.Date;\n\n/**\n * SharingMetricItem\n * 技术线下分享指标项\n * @author Frank Zhang\n * @date 2018-07-04 3:20 PM\n */\n@Data\npublic class SharingMetricItem extends MetricItem {\n\n    private String sharingName;\n    private SharingScope sharingScope;\n    private Date sharingDate;\n    private String sharingLink;\n\n    public SharingMetricItem(){\n\n    }\n\n    public SharingMetricItem(String sharingName, SharingScope sharingScope, Date sharingDate, String url) {\n        this.sharingName = sharingName;\n        this.sharingScope = sharingScope;\n        this.sharingDate = sharingDate;\n        this.sharingLink = url;\n    }\n\n    public static SharingMetricItem valueOf(String json){\n        return JSON.parseObject(json, SharingMetricItem.class);\n    }\n\n    @Override\n    public double calculateScore() {\n        return sharingScope.getScore();\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/techinfluence/SharingScope.java",
    "content": "package com.alibaba.craftsman.domain.metrics.techinfluence;\n\nimport lombok.Getter;\n\n/**\n * SharingScope\n * 线下分享的范围，范围不同，分值不同\n * @author Frank Zhang\n * @date 2018-07-04 3:25 PM\n */\npublic enum SharingScope {\n    TEAM(2, \"团队内分享\"),\n    BU(20, \"BU内部分享\"),\n    ALIBABA(30, \"集团内部分享\"),\n    COMMUNITY(40, \"公众外部分享\");\n\n\n    @Getter\n    private double score;\n    private String desc;\n\n    private SharingScope(double score, String desc) {\n        this.score = score;\n        this.desc = desc;\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/weight/DevWeight.java",
    "content": "package com.alibaba.craftsman.domain.metrics.weight;\n\n/**\n * 开发的总分计算权重\n */\npublic class DevWeight extends Weight{\n\n    public static DevWeight singleton= new DevWeight();\n\n    @Override\n    public double getAppQualityWeight() {\n        return  20 / WEIGHT_PERCENTAGE;\n    }\n\n    @Override\n    public double getTechInfluenceWeight() {\n        return 30 / WEIGHT_PERCENTAGE;\n    }\n\n    @Override\n    public double getTechContributionWeight() {\n        return 30 / WEIGHT_PERCENTAGE;\n    }\n\n    @Override\n    public double getDevQualityWeight() {\n        return 20 / WEIGHT_PERCENTAGE;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/weight/OtherWeight.java",
    "content": "package com.alibaba.craftsman.domain.metrics.weight;\n\n/**\n * 非技术人员不需要考核\n */\npublic class OtherWeight extends Weight{\n\n    public static OtherWeight singleton= new OtherWeight();\n\n    @Override\n    public double getAppQualityWeight() {\n        return 0;\n    }\n\n    @Override\n    public double getTechInfluenceWeight() {\n        return 0;\n    }\n\n    @Override\n    public double getTechContributionWeight() {\n        return 0;\n    }\n\n    @Override\n    public double getDevQualityWeight() {\n        return 0;\n    }\n}\n\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/weight/QAWeight.java",
    "content": "package com.alibaba.craftsman.domain.metrics.weight;\n\n/**\n * 测试的分数权重占比\n */\npublic class QAWeight extends Weight{\n\n    public static QAWeight singleton= new QAWeight();\n\n    @Override\n    public double getAppQualityWeight() {\n        return 10 / WEIGHT_PERCENTAGE;\n    }\n\n    @Override\n    public double getTechInfluenceWeight() {\n        return 60 / WEIGHT_PERCENTAGE;\n    }\n\n    @Override\n    public double getTechContributionWeight() {\n        return 20 / WEIGHT_PERCENTAGE;\n    }\n\n    @Override\n    public double getDevQualityWeight() {\n        return 10 / WEIGHT_PERCENTAGE;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/weight/Weight.java",
    "content": "package com.alibaba.craftsman.domain.metrics.weight;\n\n/**\n * Weight 权重\n *\n * @author Frank Zhang\n * @date 2018-09-13 12:13 PM\n */\npublic abstract class Weight {\n\n    public static double WEIGHT_PERCENTAGE = 100;\n\n    public abstract double getAppQualityWeight();\n    public abstract double getTechInfluenceWeight();\n    public abstract double getTechContributionWeight();\n    public abstract double getDevQualityWeight();\n\n    public double getUnanimousWeight(){\n        return 100/WEIGHT_PERCENTAGE;\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/metrics/weight/WeightFactory.java",
    "content": "package com.alibaba.craftsman.domain.metrics.weight;\n\nimport com.alibaba.craftsman.domain.user.Role;\n\npublic class WeightFactory {\n    public static Weight get(Role role){\n        if(role == Role.DEV){\n            return DevWeight.singleton;\n        }\n        if(role == Role.QA){\n            return QAWeight.singleton;\n        }\n        return OtherWeight.singleton;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/package-info.java",
    "content": "/**\n *\n * This is the core Domain business which should be pure and clean, have no dependency over other layers.\n *\n * @author fulan.zjf\n */\npackage com.alibaba.craftsman.domain;"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/user/Role.java",
    "content": "package com.alibaba.craftsman.domain.user;\n\n/**\n * Role Enumeration\n *\n * @author Frank Zhang\n * @date 2018-09-13 12:25 PM\n */\npublic enum Role {\n    DEV(\"开发\"),\n    QA( \"测试\"),\n    OTHER(\"非技术岗\");\n\n    public String desc;\n\n    Role(String desc){\n        this.desc = desc;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/main/java/com/alibaba/craftsman/domain/user/UserProfile.java",
    "content": "package com.alibaba.craftsman.domain.user;\n\nimport com.alibaba.cola.domain.Entity;\nimport com.alibaba.cola.exception.Assert;\nimport com.alibaba.craftsman.domain.metrics.appquality.AppQualityMetric;\nimport com.alibaba.craftsman.domain.metrics.devquality.DevQualityMetric;\nimport com.alibaba.craftsman.domain.metrics.weight.Weight;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.ContributionMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.InfluenceMetric;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * 员工档案\n * @author frankzhang\n */\n@Data\n@NoArgsConstructor\n@Entity\n@Slf4j\npublic class UserProfile {\n\n    private String id;\n    private String userId;\n    private String userName;\n    private String dep;\n    private Role role;\n    private String isManager;\n    private Weight weight;\n    private double totalScore;\n    private double appQualityScore;\n    private double techInfluenceScore;\n    private double techContributionScore;\n    private double devQualityScore;\n    private double checkinCodeQuantity;\n\n    private AppQualityMetric appQualityMetric;\n    private InfluenceMetric influenceMetric;\n    private ContributionMetric contributionMetric;\n    private DevQualityMetric devQualityMetric;\n\n    private static final double MAXIMUM_SCORE = 100;\n    private static final double MINIMUM_SCORE = 0;\n\n    public void calculateScore(){\n        calculateTechInfluenceScore();\n        calculateTechContributionScore();\n        calculateDevQualityMetric();\n        calculateAppQualityMetric();\n        calculateTotalScore();\n    }\n\n    private void calculateAppQualityMetric() {\n        Assert.notNull(appQualityMetric, \"appQualityMetric is null, initialize it before calculating\");\n        appQualityScore = appQualityMetric.calculateScore();\n    }\n\n    private void calculateDevQualityMetric(){\n        Assert.notNull(devQualityMetric, \"devQualityMetric is null, initialize it before calculating\");\n        devQualityScore = devQualityMetric.calculateScore();\n    }\n\n    private void calculateTechInfluenceScore(){\n        Assert.notNull(influenceMetric, \"influenceMetric is null, initialize it before calculating\");\n        techInfluenceScore = influenceMetric.calculateScore();\n    }\n\n    private void calculateTechContributionScore(){\n        Assert.notNull(contributionMetric, \"contributionMetric is null, initialize it before calculating\");\n        techContributionScore = contributionMetric.calculateScore();\n    }\n\n    private void calculateTotalScore(){\n        totalScore = round(this.techInfluenceScore) * influenceMetric.getWeight()\n                + round(this.techContributionScore) * contributionMetric.getWeight()\n                + round(this.devQualityScore) * devQualityMetric.getWeight()\n                + round(this.appQualityScore) * appQualityMetric.getWeight();\n    }\n\n    private double round(double score){\n        if(score > MAXIMUM_SCORE){\n            score = MAXIMUM_SCORE;\n        }else if(score < MINIMUM_SCORE){\n            score = MINIMUM_SCORE;\n        }\n        return score;\n    }\n\n    public UserProfile(String userId){\n        this.userId = userId;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/test/java/com/alibaba/craftsman/domain/ATAMetricTest.java",
    "content": "package com.alibaba.craftsman.domain;\n\nimport com.alibaba.craftsman.domain.metrics.techinfluence.ATAMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.ATAMetricItem;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.InfluenceMetric;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * ATAMetricTest\n *\n * @author Frank Zhang\n * @date 2019-02-26 2:07 PM\n */\npublic class ATAMetricTest {\n\n    @Test\n    public void testBasicScore(){\n        ATAMetricItem ataMetricItem = new ATAMetricItem(\"article\",19,99,14,2) ;\n        Assert.assertEquals(0.5, ataMetricItem.calculateScore(), 0.01);\n    }\n\n    @Test\n    public void testNormalScore(){\n        ATAMetricItem ataMetricItem = new ATAMetricItem(\"article\",20,100,15,3) ;\n        Assert.assertEquals(1.5, ataMetricItem.calculateScore(), 0.01);\n    }\n\n    @Test\n    public void testPopularScore(){\n        ATAMetricItem ataMetricItem = new ATAMetricItem(\"article\",100, 500, 75, 15) ;\n        Assert.assertEquals(5.5, ataMetricItem.calculateScore(), 0.01);\n    }\n\n    @Test\n    public void testJSON(){\n        ATAMetricItem ataMetricItem = new ATAMetricItem();\n        ataMetricItem.setTitle(\"title\");\n        ataMetricItem.setUrl(\"sharingLink\");\n        ataMetricItem.setFavoriteCount(1000);\n        ataMetricItem.setCommentCount(203);\n        ataMetricItem.setSubMetric(new ATAMetric(new InfluenceMetric(new UserProfile(\"78492\"))));\n\n        String jsonStr = ataMetricItem.toJsonString();\n        ATAMetricItem jsonObject = ATAMetricItem.valueOf(jsonStr);\n\n        Assert.assertEquals(ataMetricItem.getTitle(), jsonObject.getTitle());\n        Assert.assertEquals(ataMetricItem.getUrl(), jsonObject.getUrl());\n        Assert.assertEquals(ataMetricItem.getFavoriteCount(), jsonObject.getFavoriteCount());\n        Assert.assertEquals(ataMetricItem.getCommentCount(), jsonObject.getCommentCount());\n        Assert.assertEquals(ataMetricItem.getHitCount(), jsonObject.getHitCount());\n        Assert.assertEquals(ataMetricItem, jsonObject);\n    }\n\n    @Test\n    public void testATAMetric(){\n        ATAMetric ataMetric = new ATAMetric(new InfluenceMetric(new UserProfile()));\n        ataMetric.addMetricItem( new ATAMetricItem(\"article\",19,99,14,2));\n        ataMetric.addMetricItem( new ATAMetricItem(\"article\",20,100,15,3) );\n        ataMetric.addMetricItem( new ATAMetricItem(\"article\",100, 500, 75, 15) );\n        Assert.assertEquals(7.5, ataMetric.calculateScore(), 0.01);\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/test/java/com/alibaba/craftsman/domain/AppMetricTest.java",
    "content": "package com.alibaba.craftsman.domain;\n\nimport com.alibaba.craftsman.domain.metrics.appquality.AppMetric;\nimport com.alibaba.craftsman.domain.metrics.appquality.AppMetricItem;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class AppMetricTest {\n\n    @Test\n    public void testAppMetricItem(){\n        AppMetricItem appMetricItem = new AppMetricItem();\n        appMetricItem.setAppName(\"app1\");\n        appMetricItem.setCyclomaticComplexityCount(200);\n        appMetricItem.setDuplicatedMethodCount(80);\n        appMetricItem.setLongMethodCount(70);\n        appMetricItem.setBlockedCodeConductCount(20);\n\n        Assert.assertEquals(63, appMetricItem.calculateScore(), 0.01);\n    }\n\n    @Test\n    public void testAppMetric(){\n        AppMetricItem appMetricItem1 = new AppMetricItem();\n        appMetricItem1.setAppName(\"app1\");\n        appMetricItem1.setCyclomaticComplexityCount(200);\n        appMetricItem1.setDuplicatedMethodCount(80);\n        appMetricItem1.setLongMethodCount(70);\n        appMetricItem1.setBlockedCodeConductCount(20);\n        appMetricItem1.calculateScore();\n\n        AppMetricItem appMetricItem2 = new AppMetricItem();\n        appMetricItem2.setAppName(\"app2\");\n        appMetricItem2.setCyclomaticComplexityCount(20);\n        appMetricItem2.setDuplicatedMethodCount(30);\n        appMetricItem2.setLongMethodCount(7);\n        appMetricItem2.setBlockedCodeConductCount(5);\n        appMetricItem2.calculateScore();\n\n        AppMetric appMetric = new AppMetric();\n        appMetric.addMetricItem(appMetricItem1);\n        appMetric.addMetricItem(appMetricItem2);\n\n        Assert.assertEquals(79, appMetric.calculateScore(), 0.01);\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/test/java/com/alibaba/craftsman/domain/BugMetricTest.java",
    "content": "package com.alibaba.craftsman.domain;\n\nimport com.alibaba.craftsman.domain.metrics.devquality.BugMetricItem;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class BugMetricTest {\n\n    @Test\n    public void test5BugsPer1000LinesCode(){\n        BugMetricItem bugMetricItem = new BugMetricItem(5, 1000);\n        Assert.assertEquals(75, bugMetricItem.calculateScore(), 0.01);\n    }\n\n    @Test\n    public void test2BugsPer1000LinesCode(){\n        BugMetricItem bugMetricItem = new BugMetricItem(2, 1000);\n        Assert.assertEquals(90, bugMetricItem.calculateScore(), 0.01);\n    }\n\n    @Test\n    public void test5BugsPer10000LinesCode(){\n        BugMetricItem bugMetricItem = new BugMetricItem(5, 10000);\n        Assert.assertEquals(100, bugMetricItem.calculateScore(), 0.01);\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/test/java/com/alibaba/craftsman/domain/InfluenceMetricTest.java",
    "content": "package com.alibaba.craftsman.domain;\n\nimport com.alibaba.craftsman.domain.metrics.techinfluence.*;\nimport com.alibaba.craftsman.domain.metrics.weight.DevWeight;\nimport com.alibaba.craftsman.domain.metrics.weight.QAWeight;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.Date;\n\npublic class InfluenceMetricTest {\n\n    @Test\n    public void testDevScore(){\n        UserProfile userProfile = new UserProfile();\n        userProfile.setWeight(new DevWeight());\n        InfluenceMetric influenceMetric = new InfluenceMetric(userProfile);\n        prepareSubMetrics(influenceMetric);\n\n        Assert.assertEquals(124.5, influenceMetric.calculateScore(), 0.01);\n    }\n\n    @Test\n    public void testQAScore(){\n        UserProfile userProfile = new UserProfile();\n        userProfile.setWeight(new QAWeight());\n        InfluenceMetric influenceMetric = new InfluenceMetric(userProfile);\n        prepareSubMetrics(influenceMetric);\n\n        Assert.assertEquals(124.5, influenceMetric.calculateScore(), 0.01);\n    }\n\n    public static void prepareSubMetrics(InfluenceMetric influenceMetric) {\n        ATAMetric ataMetric = new ATAMetric(influenceMetric);\n        ataMetric.addMetricItem( new ATAMetricItem(\"article\",19,99,14,2));\n        ataMetric.addMetricItem( new ATAMetricItem(\"article\",20,100,15,3) );\n        ataMetric.addMetricItem( new ATAMetricItem(\"article\",100, 500, 75, 15) );\n\n        PatentMetric patentMetric = new PatentMetric(influenceMetric);\n        patentMetric.addMetricItem(new PatentMetricItem(\"patentName\",\"patentDesc\",\"patentNo\",\"sharingLink\", AuthorType.FIRST_AUTHOR));\n        patentMetric.addMetricItem(new PatentMetricItem(\"patentName\",\"patentDesc\",\"patentNo\",\"sharingLink\", AuthorType.OTHER_AUTHOR));\n\n        SharingMetric sharingMetric = new SharingMetric(influenceMetric);\n        sharingMetric.addMetricItem(new SharingMetricItem(\"title\", SharingScope.TEAM, new Date(), \"sharingLink\"));\n        sharingMetric.addMetricItem(new SharingMetricItem(\"title\", SharingScope.BU, new Date(), \"sharingLink\"));\n        sharingMetric.addMetricItem(new SharingMetricItem(\"title\", SharingScope.ALIBABA, new Date(), \"sharingLink\"));\n        sharingMetric.addMetricItem(new SharingMetricItem(\"title\", SharingScope.COMMUNITY, new Date(), \"sharingLink\"));\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/test/java/com/alibaba/craftsman/domain/PatentMetricTest.java",
    "content": "package com.alibaba.craftsman.domain;\n\nimport com.alibaba.craftsman.domain.metrics.techinfluence.*;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * PatentMetricTest\n *\n * @author Frank Zhang\n * @date 2019-02-26 4:20 PM\n */\npublic class PatentMetricTest {\n\n    @Test\n    public void testPatentMetric(){\n        PatentMetric patentMetric = new PatentMetric(new InfluenceMetric(new UserProfile()));\n        patentMetric.addMetricItem(new PatentMetricItem(\"patentName\",\"patentDesc\",\"patentNo\",\"sharingLink\", AuthorType.FIRST_AUTHOR));\n        patentMetric.addMetricItem(new PatentMetricItem(\"patentName\",\"patentDesc\",\"patentNo\",\"sharingLink\", AuthorType.OTHER_AUTHOR));\n\n        Assert.assertEquals(25, patentMetric.calculateScore(), 0.01);\n\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/test/java/com/alibaba/craftsman/domain/SharingMetricTest.java",
    "content": "package com.alibaba.craftsman.domain;\n\nimport com.alibaba.craftsman.domain.metrics.techinfluence.InfluenceMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.SharingMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.SharingMetricItem;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.SharingScope;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.Date;\n\n/**\n * SharingMetricTest\n *\n * @author Frank Zhang\n * @date 2019-02-26 4:14 PM\n */\npublic class SharingMetricTest {\n\n    @Test\n    public void testSharingMetric(){\n        SharingMetric sharingMetric = new SharingMetric(new InfluenceMetric(new UserProfile()));\n        sharingMetric.addMetricItem(new SharingMetricItem(\"title\", SharingScope.TEAM, new Date(), \"sharingLink\"));\n        sharingMetric.addMetricItem(new SharingMetricItem(\"title\", SharingScope.BU, new Date(), \"sharingLink\"));\n        sharingMetric.addMetricItem(new SharingMetricItem(\"title\", SharingScope.ALIBABA, new Date(), \"sharingLink\"));\n        sharingMetric.addMetricItem(new SharingMetricItem(\"title\", SharingScope.COMMUNITY, new Date(), \"sharingLink\"));\n\n        Assert.assertEquals(92, sharingMetric.calculateScore(), 0.01);\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-domain/src/test/java/com/alibaba/craftsman/domain/UserProfileTest.java",
    "content": "package com.alibaba.craftsman.domain;\n\nimport com.alibaba.cola.exception.BizException;\nimport com.alibaba.craftsman.domain.metrics.appquality.AppMetric;\nimport com.alibaba.craftsman.domain.metrics.appquality.AppMetricItem;\nimport com.alibaba.craftsman.domain.metrics.appquality.AppQualityMetric;\nimport com.alibaba.craftsman.domain.metrics.devquality.BugMetric;\nimport com.alibaba.craftsman.domain.metrics.devquality.BugMetricItem;\nimport com.alibaba.craftsman.domain.metrics.devquality.DevQualityMetric;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.CodeReviewMetric;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.CodeReviewMetricItem;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.ContributionMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.ATAMetric;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.ATAMetricItem;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.InfluenceMetric;\nimport com.alibaba.craftsman.domain.metrics.weight.DevWeight;\nimport com.alibaba.craftsman.domain.metrics.weight.QAWeight;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * UserProfileTest\n *\n * @author Frank Zhang\n * @date 2020-08-28 2:03 PM\n */\npublic class UserProfileTest {\n\n    @Test\n    public void testCalculateScore(){\n        UserProfile userProfile = new UserProfile();\n        userProfile.setWeight(new DevWeight());\n\n        //App Quality Metric\n        AppMetricItem appMetricItem1 = new AppMetricItem();\n        appMetricItem1.setAppName(\"app1\");\n        appMetricItem1.setCyclomaticComplexityCount(200);\n        appMetricItem1.setDuplicatedMethodCount(80);\n        appMetricItem1.setLongMethodCount(70);\n        appMetricItem1.setBlockedCodeConductCount(20);\n        appMetricItem1.calculateScore();\n\n        AppMetricItem appMetricItem2 = new AppMetricItem();\n        appMetricItem2.setAppName(\"app2\");\n        appMetricItem2.setCyclomaticComplexityCount(20);\n        appMetricItem2.setDuplicatedMethodCount(30);\n        appMetricItem2.setLongMethodCount(7);\n        appMetricItem2.setBlockedCodeConductCount(5);\n        appMetricItem2.calculateScore();\n\n        AppMetric appMetric = new AppMetric();\n        appMetric.addMetricItem(appMetricItem1);\n        appMetric.addMetricItem(appMetricItem2);\n\n        AppQualityMetric appQualityMetric = new AppQualityMetric(userProfile);\n        appMetric.setParent(appQualityMetric);\n\n        //influence Metric\n        InfluenceMetric influenceMetric = new InfluenceMetric(userProfile);\n        InfluenceMetricTest.prepareSubMetrics(influenceMetric);\n\n        //techContribution Metric\n        CodeReviewMetric codeReviewMetric = new CodeReviewMetric();\n        CodeReviewMetricItem codeReviewMetricItem = new CodeReviewMetricItem();\n        codeReviewMetricItem.setNoteCount(4);\n        codeReviewMetricItem.setReviewId(\"12234455\");\n        codeReviewMetric.addMetricItem(codeReviewMetricItem);\n        ContributionMetric contributionMetric = new ContributionMetric(userProfile);\n\n        //dev quality metric\n        DevQualityMetric devQualityMetric = new DevQualityMetric(userProfile);\n        BugMetric bugMetric = new BugMetric();\n        BugMetricItem bugMetricItem = new BugMetricItem(2, 1000);\n        bugMetric.addMetricItem(bugMetricItem);\n        devQualityMetric.setBugMetric(bugMetric);\n\n        //Execution\n        userProfile.calculateScore();\n\n        //Assertion\n        Assert.assertEquals(45.8, userProfile.getTotalScore(), 0.01);\n    }\n\n    @Test(expected = BizException.class)\n    public void testNPE(){\n        UserProfile userProfile = new UserProfile();\n        userProfile.setWeight(new DevWeight());\n\n        userProfile.calculateScore();\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>com.alibaba.craftsman</groupId>\n        <artifactId>craftsman.all</artifactId>\n        <version>1.0.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>craftsman-infrastructure</artifactId>\n    <packaging>jar</packaging>\n    <name>craftsman-infrastructure</name>\n\n    <dependencies>\n        <!--DIP here, Infrastructure depends on Domain-->\n        <dependency>\n            <groupId>com.alibaba.craftsman</groupId>\n            <artifactId>craftsman-domain</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.craftsman</groupId>\n            <artifactId>craftsman-client</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/common/BizCode.java",
    "content": "package com.alibaba.craftsman.common;\n\npublic class BizCode {\n\n    public final static String BIZ_ONE = \"ali.cola.demo.bizOne\"; //biz one\n\n    public final static String BIZ_TWO = \"ali.cola.demo.bizTwo\"; //biz two\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/common/event/DomainEventPublisher.java",
    "content": "package com.alibaba.craftsman.common.event;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * DomainEventPublisher, this is for demo purpose, the Event is sent to EventBus\n *\n * Normally DomainEvent should be sent to Messaging Middleware\n *\n * @author Frank Zhang\n * @date 2019-01-04 11:05 AM\n */\n@Component\npublic class DomainEventPublisher{\n\n    public void publish(Object domainEvent) {\n        //eventBus.fire(domainEvent);\n    }\n}"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/common/exception/ErrorCode.java",
    "content": "package com.alibaba.craftsman.common.exception;\n\n/**\n * ErrorCode\n *\n * @author Frank Zhang\n * @date 2019-01-04 11:00 AM\n */\npublic enum ErrorCode {\n\n    B_CUSTOMER_companyNameConflict(\"B_CUSTOMER_companyNameConflict\", \"客户公司名冲突\");\n\n    private final String errCode;\n    private final String errDesc;\n\n    private ErrorCode(String errCode, String errDesc) {\n        this.errCode = errCode;\n        this.errDesc = errDesc;\n    }\n\n    public String getErrCode() {\n        return errCode;\n    }\n\n    public String getErrDesc() {\n        return errDesc;\n    }\n\n    public static ErrorCode statOf(String ecode) {\n        for (ErrorCode errorCode : values()){\n            if (errorCode.getErrCode().equals(ecode))\n                return errorCode;\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/config/CraftsmanConfig.java",
    "content": "package com.alibaba.craftsman.config;\n\n\n/**\n * Configuration for infrastructure\n */\npublic class CraftsmanConfig {\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/convertor/MetricConvertor.java",
    "content": "package com.alibaba.craftsman.convertor;\n\n\nimport com.alibaba.craftsman.domain.metrics.MetricItem;\nimport com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO;\n\n/**\n * @author frankzhang\n */\npublic class MetricConvertor{\n\n    public static MetricDO toDataObject(MetricItem metricItem){\n        MetricDO metricDO = new MetricDO();\n        metricDO.setUserId(metricItem.getMetricOwner().getUserId());\n        metricDO.setMainMetric(metricItem.getSubMetric().getParent().getCode());\n        metricDO.setSubMetric(metricItem.getSubMetric().getCode());\n        metricDO.setMetricItem(metricItem.toJsonString());\n        metricDO.setCreator(\"test\");\n        metricDO.setModifier(\"test\");\n        return metricDO;\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/convertor/UserProfileConvertor.java",
    "content": "package com.alibaba.craftsman.convertor;\n\nimport com.alibaba.craftsman.domain.user.Role;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.craftsman.dto.clientobject.UserProfileCO;\nimport com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO;\nimport org.springframework.beans.BeanUtils;\n\npublic class UserProfileConvertor{\n\n    public static UserProfile toEntity(UserProfileCO userProfileCO){\n        UserProfile userProfile = new UserProfile();\n        BeanUtils.copyProperties(userProfileCO, userProfile);\n        userProfile.setRole(Role.valueOf(userProfileCO.getRole()));\n        return userProfile;\n    }\n\n    public static UserProfileDO toDataObject(UserProfile userProfile){\n        UserProfileDO userProfileDO = new UserProfileDO();\n        BeanUtils.copyProperties(userProfile, userProfileDO);\n        userProfileDO.setRole(userProfile.getRole().name());\n        return userProfileDO;\n    }\n\n    public static UserProfileDO toDataObjectForCreate(UserProfile userProfile){\n        UserProfileDO userProfileDO = toDataObject(userProfile);\n        return userProfileDO;\n    }\n\n    public static UserProfileDO  toDataObjectForUpdate(UserProfile userProfile){\n        UserProfileDO userProfileDO = toDataObject(userProfile);\n        return userProfileDO;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/gatewayimpl/MetricGatewayImpl.java",
    "content": "package com.alibaba.craftsman.gatewayimpl;\n\nimport com.alibaba.craftsman.common.event.DomainEventPublisher;\nimport com.alibaba.craftsman.convertor.MetricConvertor;\nimport com.alibaba.craftsman.domain.gateway.MetricGateway;\nimport com.alibaba.craftsman.domain.metrics.MainMetricType;\nimport com.alibaba.craftsman.domain.metrics.MetricItem;\nimport com.alibaba.craftsman.domain.metrics.SubMetric;\nimport com.alibaba.craftsman.domain.metrics.SubMetricType;\nimport com.alibaba.craftsman.domain.metrics.appquality.AppMetric;\nimport com.alibaba.craftsman.domain.metrics.appquality.AppMetricItem;\nimport com.alibaba.craftsman.domain.metrics.devquality.BugMetric;\nimport com.alibaba.craftsman.domain.metrics.devquality.BugMetricItem;\nimport com.alibaba.craftsman.domain.metrics.techcontribution.*;\nimport com.alibaba.craftsman.domain.metrics.techinfluence.*;\nimport com.alibaba.craftsman.dto.domainevent.MetricItemCreatedEvent;\nimport com.alibaba.craftsman.gatewayimpl.database.MetricMapper;\nimport com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO;\nimport com.alibaba.craftsman.gatewayimpl.rpc.AppMetricMapper;\nimport com.alibaba.craftsman.gatewayimpl.rpc.BugMetricMapper;\nimport com.alibaba.craftsman.gatewayimpl.rpc.dataobject.AppMetricDO;\nimport com.alibaba.craftsman.gatewayimpl.rpc.dataobject.BugMetricDO;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * MetricGatewayImpl\n *\n * @author Frank Zhang\n * @date 2020-07-02 12:20 PM\n */\n@Component\n@Slf4j\npublic class MetricGatewayImpl implements MetricGateway {\n\n    @Resource\n    private MetricMapper metricMapper;\n\n    @Resource\n    private BugMetricMapper bugMetricMapper;\n\n    @Resource\n    private AppMetricMapper appMetricMapper;\n\n    @Resource\n    private DomainEventPublisher domainEventPublisher;\n\n\n    @Override\n    public void save(MetricItem metricItem){\n        MetricDO metricDO = MetricConvertor.toDataObject(metricItem);\n\n        metricMapper.create(metricDO);\n\n        log.debug(\"AutoGeneratedId: \"+metricDO.getId());\n        MetricItemCreatedEvent metricItemCreatedEvent = new MetricItemCreatedEvent();\n        metricItemCreatedEvent.setId(metricDO.getId());\n        metricItemCreatedEvent.setUserId(metricDO.getUserId());\n        metricItemCreatedEvent.setMainMetricType(metricDO.getMainMetric());\n        domainEventPublisher.publish(metricItemCreatedEvent);\n    }\n\n    @Override\n    public List<SubMetric> listByTechContribution(String userId){\n        List<MetricDO> metricDOList = metricMapper.listByMainMetric(userId, MainMetricType.TECH_CONTRIBUTION.getMetricCode());\n        RefactoringMetric refactoringMetric = new RefactoringMetric();\n        MiscMetric miscMetric = new MiscMetric();\n        CodeReviewMetric codeReviewMetric = new CodeReviewMetric();\n        List<SubMetric> subMetricList = new ArrayList<>();\n        subMetricList.add(refactoringMetric);\n        subMetricList.add(miscMetric);\n        subMetricList.add(codeReviewMetric);\n        metricDOList.forEach(metricDO -> {\n            String json = metricDO.getMetricItem();\n            switch (SubMetricType.valueOf(metricDO.getSubMetric())){\n                case Refactoring:\n                    refactoringMetric.addMetricItem(RefactoringMetricItem.valueOf(json));\n                    break;\n                case Misc:\n                    miscMetric.addMetricItem(MiscMetricItem.valueOf(json));\n                    break;\n                case CodeReview:\n                    codeReviewMetric.addMetricItem(CodeReviewMetricItem.valueOf(json));\n                    break;\n                default:\n                    log.error(\"illegal SubMetric type: \" + metricDO.getSubMetric());\n            }\n        });\n        return subMetricList;\n    }\n\n    @Override\n    public List<SubMetric> listByTechInfluence(String userId){\n        List<MetricDO> metricDOList = metricMapper.listByMainMetric(userId, MainMetricType.TECH_INFLUENCE.getMetricCode());\n        ATAMetric ataMetric = new ATAMetric();\n        SharingMetric sharingMetric = new SharingMetric();\n        PatentMetric patentMetric = new PatentMetric();\n        PaperMetric paperMetric = new PaperMetric();\n        List<SubMetric> subMetricList = new ArrayList<>();\n        subMetricList.add(ataMetric);\n        subMetricList.add(sharingMetric);\n        subMetricList.add(patentMetric);\n        subMetricList.add(paperMetric);\n        metricDOList.forEach(metricDO -> {\n            String json = metricDO.getMetricItem();\n            switch (SubMetricType.valueOf(metricDO.getSubMetric())){\n                case ATA:\n                    ataMetric.addMetricItem(ATAMetricItem.valueOf(json));\n                    break;\n                case Sharing:\n                    sharingMetric.addMetricItem(SharingMetricItem.valueOf(json));\n                    break;\n                case Patent:\n                    patentMetric.addMetricItem(PatentMetricItem.valueOf(json));\n                    break;\n                case Paper:\n                    paperMetric.addMetricItem(PaperMetricItem.valueOf(json));\n                default:\n                    log.error(\"illegal SubMetric type: \" + metricDO.getSubMetric());\n            }\n        });\n        return subMetricList;\n    }\n\n    @Override\n    public BugMetric getBugMetric(String userId){\n        BugMetricDO bugMetricDO = bugMetricMapper.getByUserId(userId);\n        BugMetricItem bugMetricItem = new BugMetricItem(bugMetricDO.getBugCount(), bugMetricDO.getCheckInCodeCount());\n        BugMetric bugMetric = new BugMetric();\n        bugMetric.addMetricItem(bugMetricItem);\n        return bugMetric;\n    }\n\n    @Override\n    public AppMetric getAppMetric(String userId){\n        List<AppMetricDO> appMetricDOList = appMetricMapper.listByUserId(userId);\n        AppMetric appMetric = new AppMetric();\n        appMetricDOList.forEach(appMetricDO -> {\n            AppMetricItem appMetricItem = new AppMetricItem();\n            BeanUtils.copyProperties(appMetricDO, appMetricItem);\n            appMetric.addMetricItem(appMetricItem);\n        });\n        return appMetric;\n    }\n\n    @Override\n    public void delete(String id, String operator) {\n        metricMapper.delete(id, operator);\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/gatewayimpl/UserProfileGatewayImpl.java",
    "content": "package com.alibaba.craftsman.gatewayimpl;\n\nimport com.alibaba.craftsman.convertor.UserProfileConvertor;\nimport com.alibaba.craftsman.domain.DomainFactory;\nimport com.alibaba.craftsman.domain.gateway.UserProfileGateway;\nimport com.alibaba.craftsman.domain.metrics.weight.WeightFactory;\nimport com.alibaba.craftsman.domain.user.Role;\nimport com.alibaba.craftsman.domain.user.UserProfile;\nimport com.alibaba.craftsman.gatewayimpl.database.UserProfileMapper;\nimport com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n/**\n * UserProfileGatewayImpl\n *\n * @author Frank Zhang\n * @date 2020-07-02 12:32 PM\n */\n@Component\n@Slf4j\npublic class UserProfileGatewayImpl implements UserProfileGateway {\n\n    @Resource\n    private UserProfileMapper userProfileMapper;\n\n\n    @Override\n    public void create(UserProfile userProfile) {\n        userProfileMapper.create(UserProfileConvertor.toDataObjectForCreate(userProfile));\n    }\n\n    @Override\n    public void update(UserProfile userProfile) {\n        userProfileMapper.update(UserProfileConvertor.toDataObjectForUpdate(userProfile));\n    }\n\n    @Override\n    public UserProfile getByUserId(String userId) {\n        UserProfileDO userProfileDO = userProfileMapper.getByUserId(userId);\n        if (userProfileDO == null) {\n            log.warn(\"There is no UserProfile for : \" + userId);\n            return null;\n        }\n        UserProfile userProfile = DomainFactory.getUserProfile();\n        BeanUtils.copyProperties(userProfileDO, userProfile);\n        Role role = Role.valueOf(userProfileDO.getRole());\n        userProfile.setRole(role);\n        userProfile.setWeight(WeightFactory.get(role));\n        return userProfile;\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/gatewayimpl/database/MetricMapper.java",
    "content": "package com.alibaba.craftsman.gatewayimpl.database;\n\nimport com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n@Mapper\npublic interface MetricMapper {\n\n    int create(MetricDO dataObject);\n\n    List<MetricDO> listByUserId(@Param(\"userId\") String userId);\n\n    List<MetricDO> listByMainMetric(@Param(\"userId\") String userId, @Param(\"mainMetric\") String mainMetric);\n\n    List<MetricDO> listBySubMetric(@Param(\"userId\") String userId, @Param(\"subMetric\") String subMetric);\n\n    int delete(@Param(\"id\") String id, @Param(\"modifier\") String modifier);\n\n    MetricDO getById(@Param(\"id\") String id);\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/gatewayimpl/database/UserProfileMapper.java",
    "content": "package com.alibaba.craftsman.gatewayimpl.database;\n\nimport com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * UserProfileMapper\n *\n * @author Frank Zhang\n * @date 2019-02-27 5:03 PM\n */\n@Mapper\npublic interface UserProfileMapper {\n    int create(UserProfileDO userProfileDO);\n\n    int update(UserProfileDO userProfileDO);\n\n    int delete(@Param(\"userId\") String userId);\n\n    UserProfileDO getByUserId(@Param(\"userId\") String userId);\n\n    List<UserProfileDO> listByDep(@Param(\"dep\") String dep);\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/gatewayimpl/database/dataobject/BaseDO.java",
    "content": "package com.alibaba.craftsman.gatewayimpl.database.dataobject;\n\n/**\n * BaseDO\n *\n * @author Frank Zhang\n * @date 2020-07-02 10:03 AM\n */\npublic class BaseDO {\n    private String creator;\n    private String modifier;\n\n    public String getCreator() {\n        return creator;\n    }\n\n    public void setCreator(String creator) {\n        this.creator = creator;\n    }\n\n    public String getModifier() {\n        return modifier;\n    }\n\n    public void setModifier(String modifier) {\n        this.modifier = modifier;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/gatewayimpl/database/dataobject/MetricDO.java",
    "content": "package com.alibaba.craftsman.gatewayimpl.database.dataobject;\n\n\npublic class MetricDO extends BaseDO{\n    private String id;\n    /**\n     * 域账号\n     */\n    private String userId;\n    /**\n     * 主度量\n     */\n    private String mainMetric;\n    /**\n     * 子度量\n     */\n    private String subMetric;\n    /**\n     * 度量条目\n     */\n    private String metricItem;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getUserId() {\n        return userId;\n    }\n\n    public void setUserId(String userId) {\n        this.userId = userId;\n    }\n\n    public String getMainMetric() {\n        return mainMetric;\n    }\n\n    public void setMainMetric(String mainMetric) {\n        this.mainMetric = mainMetric;\n    }\n\n    public String getSubMetric() {\n        return subMetric;\n    }\n\n    public void setSubMetric(String subMetric) {\n        this.subMetric = subMetric;\n    }\n\n    public String getMetricItem() {\n        return metricItem;\n    }\n\n    public void setMetricItem(String metricItem) {\n        this.metricItem = metricItem;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/gatewayimpl/database/dataobject/UserProfileDO.java",
    "content": "package com.alibaba.craftsman.gatewayimpl.database.dataobject;\n\n\n/**\n * UserProfileDO\n *\n * @author Frank Zhang\n * @date 2019-02-27 5:00 PM\n */\n\npublic class UserProfileDO extends BaseDO{\n\n    /**\n     * 域账号\n     */\n    private String userId;\n\n    /**\n     * 姓名\n     */\n    private String userName;\n\n    /**\n     * 部门\n     */\n    private String dep;\n\n    /**\n     * 角色\n     */\n    private String role;\n\n    /**\n     * 是否主管\n     */\n    private String isManager;\n\n    /**\n     * 综合得分\n     */\n    private double totalScore;\n\n    /**\n     * 代码质量分\n     */\n    private double appQualityScore;\n\n    /**\n     * 技术影响力分\n     */\n    private double techInfluenceScore;\n\n    /**\n     * 技术贡献分\n     */\n    private double techContributionScore;\n\n    /**\n     * 开发质量分\n     */\n    private double devQualityScore;\n\n    /**\n     * checkin代码量\n     */\n    private double checkinCodeQuantity;\n\n    public String getUserId() {\n        return userId;\n    }\n\n    public void setUserId(String userId) {\n        this.userId = userId;\n    }\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public String getDep() {\n        return dep;\n    }\n\n    public void setDep(String dep) {\n        this.dep = dep;\n    }\n\n    public String getRole() {\n        return role;\n    }\n\n    public void setRole(String role) {\n        this.role = role;\n    }\n\n    public String getIsManager() {\n        return isManager;\n    }\n\n    public void setIsManager(String isManager) {\n        this.isManager = isManager;\n    }\n\n    public double getTotalScore() {\n        return totalScore;\n    }\n\n    public void setTotalScore(double totalScore) {\n        this.totalScore = totalScore;\n    }\n\n    public double getAppQualityScore() {\n        return appQualityScore;\n    }\n\n    public void setAppQualityScore(double appQualityScore) {\n        this.appQualityScore = appQualityScore;\n    }\n\n    public double getTechInfluenceScore() {\n        return techInfluenceScore;\n    }\n\n    public void setTechInfluenceScore(double techInfluenceScore) {\n        this.techInfluenceScore = techInfluenceScore;\n    }\n\n    public double getTechContributionScore() {\n        return techContributionScore;\n    }\n\n    public void setTechContributionScore(double techContributionScore) {\n        this.techContributionScore = techContributionScore;\n    }\n\n    public double getDevQualityScore() {\n        return devQualityScore;\n    }\n\n    public void setDevQualityScore(double devQualityScore) {\n        this.devQualityScore = devQualityScore;\n    }\n\n    public double getCheckinCodeQuantity() {\n        return checkinCodeQuantity;\n    }\n\n    public void setCheckinCodeQuantity(double checkinCodeQuantity) {\n        this.checkinCodeQuantity = checkinCodeQuantity;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/gatewayimpl/rpc/AppMetricMapper.java",
    "content": "package com.alibaba.craftsman.gatewayimpl.rpc;\n\nimport com.alibaba.craftsman.gatewayimpl.rpc.dataobject.AppMetricDO;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Component\npublic class AppMetricMapper {\n\n    /**\n     *  Dummy RPC Call\n     */\n    public List<AppMetricDO> listByUserId(String userId){\n        List<AppMetricDO> appMetricDOList = new ArrayList<>();\n        AppMetricDO appMetricDO1 = new AppMetricDO();\n        appMetricDO1.setAppName(\"app1\");\n        appMetricDO1.setCyclomaticComplexityCount(200);\n        appMetricDO1.setDuplicatedMethodCount(80);\n        appMetricDO1.setLongMethodCount(70);\n        appMetricDO1.setBlockedCodeConductCount(20);\n        appMetricDOList.add(appMetricDO1);\n\n        AppMetricDO appMetricDO2 = new AppMetricDO();\n        appMetricDO2.setAppName(\"app2\");\n        appMetricDO2.setCyclomaticComplexityCount(20);\n        appMetricDO2.setDuplicatedMethodCount(30);\n        appMetricDO2.setLongMethodCount(7);\n        appMetricDO2.setBlockedCodeConductCount(5);\n        appMetricDOList.add(appMetricDO2);\n        return appMetricDOList;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/gatewayimpl/rpc/BugMetricMapper.java",
    "content": "package com.alibaba.craftsman.gatewayimpl.rpc;\n\nimport com.alibaba.craftsman.gatewayimpl.rpc.dataobject.BugMetricDO;\nimport org.springframework.stereotype.Component;\n\n/**\n * 模拟一个RPC的Tunnel，也是日常业务中非常常见的场景。\n *\n * 假设Bug数和代码checkin数再另外一个系统中，没有存在本地，需要通过RPC调用才能获取。\n *\n */\n@Component\npublic class BugMetricMapper {\n\n    /**\n     *  Dummy RPC Call\n     */\n    public BugMetricDO getByUserId(String userId){\n        BugMetricDO bugMetricDO = new BugMetricDO();\n        bugMetricDO.setBugCount(3);\n        bugMetricDO.setCheckInCodeCount(1500);\n        return bugMetricDO;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/gatewayimpl/rpc/dataobject/AppMetricDO.java",
    "content": "package com.alibaba.craftsman.gatewayimpl.rpc.dataobject;\n\n\npublic class AppMetricDO {\n    private String appName;//应用名称\n    private int cyclomaticComplexityCount;//圈复杂度超标的数目\n    private int duplicatedMethodCount;//重复代码的数目\n    private int longMethodCount;//长方法的数目\n    private int blockedCodeConductCount;//不符合编码标准的数目\n\n    public String getAppName() {\n        return appName;\n    }\n\n    public void setAppName(String appName) {\n        this.appName = appName;\n    }\n\n    public int getCyclomaticComplexityCount() {\n        return cyclomaticComplexityCount;\n    }\n\n    public void setCyclomaticComplexityCount(int cyclomaticComplexityCount) {\n        this.cyclomaticComplexityCount = cyclomaticComplexityCount;\n    }\n\n    public int getDuplicatedMethodCount() {\n        return duplicatedMethodCount;\n    }\n\n    public void setDuplicatedMethodCount(int duplicatedMethodCount) {\n        this.duplicatedMethodCount = duplicatedMethodCount;\n    }\n\n    public int getLongMethodCount() {\n        return longMethodCount;\n    }\n\n    public void setLongMethodCount(int longMethodCount) {\n        this.longMethodCount = longMethodCount;\n    }\n\n    public int getBlockedCodeConductCount() {\n        return blockedCodeConductCount;\n    }\n\n    public void setBlockedCodeConductCount(int blockedCodeConductCount) {\n        this.blockedCodeConductCount = blockedCodeConductCount;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/java/com/alibaba/craftsman/gatewayimpl/rpc/dataobject/BugMetricDO.java",
    "content": "package com.alibaba.craftsman.gatewayimpl.rpc.dataobject;\n\n\npublic class BugMetricDO {\n    private int bugCount; //缺陷数量\n    private long checkInCodeCount; //check in的代码量\n\n    public int getBugCount() {\n        return bugCount;\n    }\n\n    public void setBugCount(int bugCount) {\n        this.bugCount = bugCount;\n    }\n\n    public long getCheckInCodeCount() {\n        return checkInCodeCount;\n    }\n\n    public void setCheckInCodeCount(long checkInCodeCount) {\n        this.checkInCodeCount = checkInCodeCount;\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/resources/TableCreationDDL.sql",
    "content": "CREATE TABLE `metric` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `user_id` varchar(64) NOT NULL COMMENT '域账号',\n  `main_metric` varchar(64) NOT NULL COMMENT '主度量',\n  `sub_metric` varchar(64) NOT NULL COMMENT '度量项',\n  `metric_item` json DEFAULT NULL COMMENT '度量项内容',\n  `creator` varchar(64) NOT NULL COMMENT '创建人',\n  `modifier` varchar(64) NOT NULL COMMENT '修改人',\n  `gmt_create` datetime NOT NULL COMMENT '创建时间',\n  `gmt_modified` datetime NOT NULL COMMENT '修改时间',\n  `is_deleted` char(1) NOT NULL DEFAULT 'n' COMMENT '逻辑删除',\n  PRIMARY KEY (`id`),\n  KEY `idx_username` (`user_id`)\n) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8mb4 COMMENT='度量表';\n\nCREATE TABLE `user_profile` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `user_id` varchar(64) NOT NULL COMMENT '工号',\n  `user_name` varchar(64) NOT NULL COMMENT '名字',\n  `dep` varchar(128) NOT NULL COMMENT '部门',\n  `role` varchar(6) NOT NULL COMMENT '角色',\n  `total_score` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '综合得分',\n  `app_quality_score` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '代码质量分',\n  `tech_influence_score` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '技术影响力分',\n  `tech_contribution_score` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '技术贡献分',\n  `dev_quality_score` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '开发质量分',\n  `checkin_code_quantity` decimal(9,2) NOT NULL DEFAULT '0.00' COMMENT 'checkin代码量',\n  `is_manager` char(1) DEFAULT NULL COMMENT '是否主管',\n  `creator` varchar(64) NOT NULL COMMENT '创建人',\n  `modifier` varchar(64) NOT NULL COMMENT '修改人',\n  `gmt_create` datetime NOT NULL COMMENT '创建时间',\n  `gmt_modified` datetime NOT NULL COMMENT '修改时间',\n  `is_deleted` char(1) NOT NULL DEFAULT 'n' COMMENT '逻辑删除',\n  PRIMARY KEY (`id`),\n  KEY `idx_user_id` (`user_id`)\n) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8mb4 COMMENT='用户Profile表';\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/resources/mybatis/MetricMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"com.alibaba.craftsman.gatewayimpl.database.MetricMapper\">\n    <sql id=\"ALL_COLUMNS\">\n        id,\n        user_id,\n        main_metric,\n        sub_metric,\n        metric_item,\n        creator,\n        modifier,\n        gmt_create,\n        gmt_modified,\n        is_deleted\n    </sql>\n\n    <sql id=\"COMMON_SELECT\">\n        SELECT\n        <include refid=\"ALL_COLUMNS\"/>\n        FROM metric\n    </sql>\n\n    <insert id=\"create\" useGeneratedKeys=\"true\" keyProperty=\"id\" parameterType=\"MetricDO\">\n        INSERT INTO metric(\n            <include refid=\"ALL_COLUMNS\"/>\n        ) VALUES (\n        null,\n        #{userId},\n        #{mainMetric},\n        #{subMetric},\n        #{metricItem},\n        #{creator},\n        #{modifier},\n        now(),\n        now(),\n        'n'\n        )\n    </insert>\n\n    <delete id=\"delete\" parameterType=\"map\">\n        UPDATE metric SET is_deleted='y', modifier = #{modifier} WHERE id = #{id} and IS_DELETED = 'n'\n    </delete>\n\n    <select id=\"listByUserId\" resultType=\"MetricDO\" parameterType=\"map\">\n        <include refid=\"COMMON_SELECT\"/>\n        WHERE user_id = #{userId} and IS_DELETED = 'n'\n    </select>\n\n    <select id=\"listByMainMetric\" resultType=\"MetricDO\" parameterType=\"map\">\n        <include refid=\"COMMON_SELECT\"/>\n        WHERE user_id = #{userId} and main_metric= #{mainMetric} and IS_DELETED = 'n'\n    </select>\n\n    <select id=\"listBySubMetric\" resultType=\"MetricDO\" parameterType=\"map\">\n        <include refid=\"COMMON_SELECT\"/>\n        WHERE user_id = #{userId} and sub_metric= #{subMetric} and IS_DELETED = 'n'\n    </select>\n\n    <select id=\"getById\" resultType=\"MetricDO\" parameterType=\"map\">\n        <include refid=\"COMMON_SELECT\"/>\n        WHERE id = #{id} and IS_DELETED = 'n'\n    </select>\n</mapper>"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/resources/mybatis/UserProfileMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"com.alibaba.craftsman.gatewayimpl.database.UserProfileMapper\">\n    <sql id=\"ALL_COLUMNS\">\n        id,\n        user_id,\n        user_name,\n        is_manager,\n        dep,\n        role,\n        total_score,\n        app_quality_score,\n        tech_influence_score,\n        tech_contribution_score,\n        dev_quality_score,\n        checkin_code_quantity,\n        creator,\n        modifier,\n        gmt_create,\n        gmt_modified,\n        is_deleted\n    </sql>\n\n    <sql id=\"COMMON_SELECT\">\n        SELECT\n        <include refid=\"ALL_COLUMNS\"/>\n        FROM user_profile\n    </sql>\n\n    <insert id=\"create\" parameterType=\"UserProfileDO\">\n        INSERT INTO user_profile(\n        <include refid=\"ALL_COLUMNS\"/>\n        ) VALUES (\n        null,\n        #{userId},\n        #{userName},\n        #{isManager},\n        #{dep},\n        #{role},\n        #{totalScore},\n        #{appQualityScore},\n        #{techInfluenceScore},\n        #{techContributionScore},\n        #{devQualityScore},\n        #{checkinCodeQuantity},\n        #{creator},\n        #{modifier},\n        now(),\n        now(),\n        'n'\n        )\n    </insert>\n\n    <delete id=\"delete\" parameterType=\"map\">\n        UPDATE user_profile SET is_deleted='y' WHERE user_id = #{userId} and IS_DELETED = 'n'\n    </delete>\n\n    <select id=\"getByUserId\" resultType=\"UserProfileDO\" parameterType=\"map\">\n        SELECT * FROM user_profile\n        WHERE user_id = #{userId} and IS_DELETED = 'n' LIMIT 1\n    </select>\n\n    <select id=\"listByDep\" resultType=\"UserProfileDO\" parameterType=\"map\">\n        <include refid=\"COMMON_SELECT\"/>\n        WHERE dep like '${dep}%' and IS_DELETED = 'n'\n    </select>\n\n    <update id=\"update\" parameterType=\"UserProfileDO\">\n        UPDATE user_profile SET GMT_MODIFIED = now()\n        <if test=\"userName != null\">\n            ,user_name = #{userName}\n        </if>\n        <if test=\"dep != null\">\n            ,dep = #{dep}\n        </if>\n        <if test=\"role != null\">\n            ,role = #{role}\n        </if>\n        <if test=\"isManager != null\">\n            ,is_manager = #{isManager}\n        </if>\n        <if test=\"totalScore != null and totalScore != 0\">\n            ,total_score = #{totalScore}\n        </if>\n        <if test=\"appQualityScore != null and appQualityScore != 0\">\n            ,app_quality_score = #{appQualityScore}\n        </if>\n        <if test=\"techInfluenceScore != null and techInfluenceScore != 0\">\n            ,tech_influence_score = #{techInfluenceScore}\n        </if>\n        <if test=\"techContributionScore != null and techContributionScore != 0\">\n            ,tech_contribution_score = #{techContributionScore}\n        </if>\n        <if test=\"devQualityScore != null and devQualityScore != 0\">\n            ,dev_quality_score = #{devQualityScore}\n        </if>\n        <if test=\"checkinCodeQuantity != null and checkinCodeQuantity != 0\">\n            ,checkin_code_quantity = #{checkinCodeQuantity}\n        </if>\n        <if test=\"modifier != null\">\n            ,modifier = #{modifier}\n        </if>\n        where user_id = #{userId} and is_deleted='n'\n    </update>\n\n</mapper>"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/main/resources/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!-- mybatis的配置文件 -->\n<!DOCTYPE configuration\n        PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n    <!-- 开启驼峰映射 ，否则查询结果不能转成resultType-->\n    <settings>\n        <setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>\n    </settings>\n    <typeAliases>\n        <!--项目DataObject对应的包名-->\n        <package name=\"com.alibaba.craftsman.gatewayimpl.database.dataobject\"/>\n    </typeAliases>\n    <mappers>\n        <mapper resource=\"mybatis/MetricMapper.xml\" />\n        <mapper resource=\"mybatis/UserProfileMapper.xml\" />\n    </mappers>\n</configuration>"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/test/java/com/alibaba/craftsman/gatewayimpl/Mybatis3Utils.java",
    "content": "package com.alibaba.craftsman.gatewayimpl;\n\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.util.Objects;\n\n/**\n * Mybatis3Utils\n *\n * @author Frank Zhang\n * @date 2019-02-27 4:38 PM\n */\npublic class Mybatis3Utils {\n    public static final SqlSessionFactory sqlSessionFactory;\n    public static final ThreadLocal<SqlSession> sessionThread = new ThreadLocal<>();\n\n    static {\n        try {\n            Reader reader = Resources.getResourceAsReader(\"mybatis-config-test.xml\");\n            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static SqlSession getCurrentSqlSession() {\n        SqlSession sqlSession = sessionThread.get();\n        if (Objects.isNull(sqlSession)) {\n            sqlSession = sqlSessionFactory.openSession();\n            sessionThread.set(sqlSession);\n        }\n        return sqlSession;\n    }\n\n    public static void closeCurrentSession() {\n        SqlSession sqlSession = sessionThread.get();\n        if (Objects.nonNull(sqlSession)) {\n            sqlSession.close();\n        }\n        sessionThread.set(null);\n    }\n}"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/test/java/com/alibaba/craftsman/gatewayimpl/MybatisTest.java",
    "content": "package com.alibaba.craftsman.gatewayimpl;\n\nimport com.alibaba.craftsman.gatewayimpl.database.MetricMapper;\nimport com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO;\nimport org.apache.ibatis.session.SqlSession;\nimport org.junit.After;\nimport org.junit.Before;\n\n/**\n * MybatisTest\n *\n * @author Frank Zhang\n * @date 2019-02-27 4:38 PM\n */\npublic class MybatisTest {\n    SqlSession sqlSession;\n    MetricMapper metricMapper;\n\n    @Before\n    public void before() {\n        sqlSession = Mybatis3Utils.getCurrentSqlSession();\n        metricMapper = sqlSession.getMapper(MetricMapper.class);\n    }\n\n    @After\n    public void after() {\n        Mybatis3Utils.closeCurrentSession();\n    }\n\n    //@Test\n    public void insert() {\n        MetricDO metricDO = new MetricDO();\n        metricDO.setMainMetric(\"mainTest\");\n        metricDO.setSubMetric(\"subTest\");\n        metricDO.setUserId(\"12345\");\n        metricDO.setCreator(\"Frank\");\n        metricDO.setModifier(\"Frank\");\n        metricDO.setMetricItem(\"{\\\"patentName\\\": \\\"Leads重构\\\", \\\"level\\\": \\\"PROJECT\\\"}\");\n\n        metricMapper.create(metricDO);\n        sqlSession.commit();\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/test/resources/logback-test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration debug=\"true\">\n    <!-- https://github.com/spring-projects/spring-boot/blob/v1.4.2.RELEASE/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n    <property name=\"APP_NAME\" value=\"craftsman\" />\n    <property name=\"LOG_PATH\" value=\"${user.home}/${APP_NAME}/logs\" />\n    <property name=\"LOG_FILE\" value=\"${LOG_PATH}/application.log\" />\n\n    <appender name=\"APPLICATION\"\n        class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${LOG_FILE}</file>\n        <encoder>\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>\n            <maxHistory>7</maxHistory>\n            <maxFileSize>50MB</maxFileSize>\n            <totalSizeCap>20GB</totalSizeCap>\n        </rollingPolicy>\n    </appender>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <root level=\"DEBUG\">\n        <appender-ref ref=\"CONSOLE\" />\n    </root>\n</configuration>"
  },
  {
    "path": "cola-samples/craftsman/craftsman-infrastructure/src/test/resources/mybatis-config-test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!-- mybatis的配置文件 -->\n<!DOCTYPE configuration\n        PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n    <typeAliases>\n        <!--项目DataObject对应的包名-->\n        <package name=\"com.alibaba.craftsman.gatewayimpl.database.dataobject\"/>\n    </typeAliases>\n    <!--myql数据库连接信息-->\n    <environments default=\"development\">\n        <environment id=\"development\">\n            <transactionManager type=\"JDBC\"/>\n            <dataSource type=\"POOLED\">\n                <property name=\"driver\" value=\"com.mysql.jdbc.Driver\"/>\n                <property name=\"url\" value=\"jdbc:mysql://127.0.0.1:3306/craftsman?useSSL=false\"/>\n                <property name=\"username\" value=\"root\"/>\n                <property name=\"password\" value=\"1983Root_2\"/>\n            </dataSource>\n        </environment>\n    </environments>\n    <!--配置实体映射xml路径-->\n    <mappers>\n        <mapper resource=\"mybatis/MetricMapper.xml\"></mapper>\n        <mapper resource=\"mybatis/UserProfileMapper.xml\"></mapper>\n    </mappers>\n</configuration>"
  },
  {
    "path": "cola-samples/craftsman/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.alibaba.craftsman</groupId>\n    <artifactId>craftsman.all</artifactId>\n    <packaging>pom</packaging>\n    <version>1.0.0-SNAPSHOT</version>\n    <name>craftsman.all</name>\n\n    <properties>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <maven.compiler.target>${maven.compiler.source}</maven.compiler.target>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <maven.deploy.skip>true</maven.deploy.skip>\n\n        <cola.components.version>5.x-SNAPSHOT</cola.components.version>\n\n        <spring-boot.version>2.7.5</spring-boot.version>\n        <mybatis-starter.version>2.2.2</mybatis-starter.version>\n    </properties>\n\n    <modules>\n        <module>craftsman-client</module>\n        <module>craftsman-adapter</module>\n        <module>craftsman-app</module>\n        <module>craftsman-domain</module>\n        <module>craftsman-infrastructure</module>\n        <module>start</module>\n    </modules>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <!--Project modules-->\n            <dependency>\n                <groupId>com.alibaba.craftsman</groupId>\n                <artifactId>craftsman-adapter</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.craftsman</groupId>\n                <artifactId>craftsman-client</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.craftsman</groupId>\n                <artifactId>craftsman-app</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.craftsman</groupId>\n                <artifactId>craftsman-domain</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.craftsman</groupId>\n                <artifactId>craftsman-infrastructure</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <!--Project modules End-->\n            <dependency>\n                <groupId>com.alibaba.cola</groupId>\n                <artifactId>cola-components-bom</artifactId>\n                <version>${cola.components.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-dependencies</artifactId>\n                <version>${spring-boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.mybatis.spring.boot</groupId>\n                <artifactId>mybatis-spring-boot-starter</artifactId>\n                <version>${mybatis-starter.version}</version>\n            </dependency>\n\n            <!--Validation API-->\n            <!--\n                - javax.validation:javax.validation\n                - org.hibernate.validator:hibernate-validator\n                - org.glassfish:jakarta.el\n                are declared in spring-boot-dependencies\n            -->\n            <dependency>\n                <groupId>javax.el</groupId>\n                <artifactId>javax.el-api</artifactId>\n                <version>3.0.0</version>\n            </dependency>\n            <!--Validation API End -->\n\n            <!-- Misc -->\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>fastjson</artifactId>\n                <version>1.2.83</version>\n            </dependency>\n            <!-- Misc End -->\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <artifactId>maven-resources-plugin</artifactId>\n                    <version>3.3.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-compiler-plugin</artifactId>\n                    <version>3.13.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-source-plugin</artifactId>\n                    <version>3.3.1</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-javadoc-plugin</artifactId>\n                    <version>3.7.0</version>\n                </plugin>\n                <plugin>\n                    <artifactId>maven-deploy-plugin</artifactId>\n                    <version>3.1.1</version>\n                </plugin>\n\n                <plugin>\n                    <groupId>org.springframework.boot</groupId>\n                    <artifactId>spring-boot-maven-plugin</artifactId>\n                    <version>${spring-boot.version}</version>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n    </build>\n\n    <profiles>\n        <profile>\n            <id>gen-java-src</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <artifactId>maven-source-plugin</artifactId>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>gen-java-doc</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <artifactId>maven-javadoc-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <id>attach-javadoc</id>\n                                <goals>\n                                    <goal>jar</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                        <configuration>\n                            <source>8</source>\n                            <show>protected</show>\n                            <charset>UTF-8</charset>\n                            <encoding>UTF-8</encoding>\n                            <docencoding>UTF-8</docencoding>\n                            <additionalJOptions>\n                                <additionalJOption>-quiet</additionalJOption>\n                                <additionalJOption>-J-Duser.language=en</additionalJOption>\n                                <additionalJOption>-J-Duser.country=US</additionalJOption>\n                                <additionalJOption>-Xdoclint:none</additionalJOption>\n                            </additionalJOptions>\n                        </configuration>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n    </profiles>\n</project>\n"
  },
  {
    "path": "cola-samples/craftsman/start/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>com.alibaba.craftsman</groupId>\n        <artifactId>craftsman.all</artifactId>\n        <version>1.0.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>start</artifactId>\n    <packaging>jar</packaging>\n    <name>start</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.alibaba.craftsman</groupId>\n            <artifactId>craftsman-adapter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/main/java/com/alibaba/craftsman/Application.java",
    "content": "package com.alibaba.craftsman;\n\nimport com.alibaba.craftsman.config.CraftsmanConfig;\nimport lombok.extern.slf4j.Slf4j;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * Spring Boot Starter\n *\n * COLA framework initialization is configured in {@link CraftsmanConfig}\n *\n * @author Frank Zhang\n */\n@SpringBootApplication(scanBasePackages = {\"com.alibaba.craftsman\",\"com.alibaba.cola\"})\n@MapperScan(\"com.alibaba.craftsman.gatewayimpl.database\")\n@Slf4j\npublic class Application {\n\n    public static void main(String[] args) {\n        log.info(\"Begin to start Spring Boot Application\");\n        long startTime = System.currentTimeMillis();\n\n        SpringApplication.run(Application.class, args);\n\n        long endTime = System.currentTimeMillis();\n        log.info(\"End starting Spring Boot Application, Time used: \"+ (endTime - startTime) );\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/main/resources/application.properties",
    "content": "project.name=start\n\n\n# mysql configruation\nspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver\nspring.datasource.url=jdbc:mysql://localhost:3306/craftsman?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull\nspring.datasource.username=root\nspring.datasource.password=1983Root_2\n\n#mybatis\nmybatis.config-location=classpath:mybatis-config.xml\n\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration >\n    <!-- https://github.com/spring-projects/spring-boot/blob/v1.4.2.RELEASE/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n    <property name=\"APP_NAME\" value=\"craftsman\" />\n    <property name=\"LOG_PATH\" value=\"${user.home}/${APP_NAME}/logs\" />\n    <property name=\"LOG_FILE\" value=\"${LOG_PATH}/application.log\" />\n\n    <appender name=\"APPLICATION\"\n        class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${LOG_FILE}</file>\n        <encoder>\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>\n            <maxHistory>7</maxHistory>\n            <maxFileSize>50MB</maxFileSize>\n            <totalSizeCap>20GB</totalSizeCap>\n        </rollingPolicy>\n    </appender>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\" />\n        <appender-ref ref=\"APPLICATION\" />\n    </root>\n</configuration>"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/java/com/alibaba/craftsman/TestApplication.java",
    "content": "package com.alibaba.craftsman;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.FilterType;\n\n@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {Application.class})})\n@SpringBootApplication(scanBasePackages = {\"com.alibaba.craftsman\"})\npublic class TestApplication {\n\n    public static void main(String[] args) {\n        //这里填的是TestApplication\n        ApplicationContext context = SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/java/com/alibaba/craftsman/gatewayimpl/MetricTunnelTest.java",
    "content": "package com.alibaba.craftsman.gatewayimpl;\n\nimport com.alibaba.craftsman.domain.metrics.MainMetricType;\nimport com.alibaba.craftsman.domain.metrics.SubMetricType;\nimport com.alibaba.craftsman.gatewayimpl.database.MetricMapper;\nimport com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO;\nimport org.junit.Assert;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.List;\n\n/**\n * This is Tunnel Test, no need to mock, and no regression needed as well\n *\n * @author Frank Zhang\n * @date 2019-02-27 2:33 PM\n */\npublic class MetricTunnelTest {\n\n    @Autowired\n    private MetricMapper metricMapper;\n\n    public void testCRUD(){\n        String userId = \"MetricTunnelTest\" + Math.random();\n        MetricDO metricDO = new MetricDO();\n        metricDO.setMainMetric(MainMetricType.TECH_INFLUENCE.getMetricCode());\n        metricDO.setSubMetric(SubMetricType.Refactoring.getMetricSubTypeCode());\n        metricDO.setUserId(userId);\n        metricDO.setMetricItem(\"{\\\"patentName\\\": \\\"Leads重构\\\", \\\"level\\\": \\\"PROJECT\\\"}\");\n\n        metricMapper.create(metricDO);\n\n        List<MetricDO> metricDOS = metricMapper.listByUserId(userId);\n        Assert.assertEquals(1, metricDOS.size());\n\n        metricMapper.delete(metricDOS.get(0).getId(),\"MetricTunnelTest\");\n        Assert.assertEquals(0, metricMapper.listByUserId(userId).size());\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/java/com/alibaba/craftsman/gatewayimpl/UserProfileTunnelTest.java",
    "content": "package com.alibaba.craftsman.gatewayimpl;\n\nimport com.alibaba.craftsman.gatewayimpl.database.UserProfileMapper;\nimport com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO;\nimport org.junit.Assert;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * This is Tunnel Test, no need to mock, and no regression needed as well\n *\n * @author Frank Zhang\n * @date 2019-02-27 5:31 PM\n */\npublic class UserProfileTunnelTest {\n    @Autowired\n    private UserProfileMapper userProfileMapper;\n\n    public void testCRUD(){\n        String userId = Math.random()+\"UserProfileTunnelTest\";\n\n        UserProfileDO userProfileDO = new UserProfileDO();\n        userProfileDO.setUserId(userId);\n        userProfileDO.setDep(\"alibaba\");\n        userProfileDO.setIsManager(\"n\");\n        userProfileDO.setUserName(\"Frank\");\n        userProfileDO.setRole(\"DEV\");\n\n        userProfileMapper.create(userProfileDO);\n\n        userProfileDO = userProfileMapper.getByUserId(userId);\n        Assert.assertEquals(userId, userProfileDO.getUserId());\n\n        userProfileMapper.delete(userId);\n        Assert.assertNull(userProfileMapper.getByUserId(userId));\n    }\n}\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/logback-test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <!-- https://github.com/spring-projects/spring-boot/blob/v1.4.2.RELEASE/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n\n    <property name=\"APP_NAME\" value=\"start\" />\n    <property name=\"LOG_PATH\" value=\"${user.home}/${APP_NAME}/logs\" />\n    <property name=\"LOG_FILE\" value=\"${LOG_PATH}/application.log\" />\n\n    <appender name=\"APPLICATION\"\n        class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${LOG_FILE}</file>\n        <encoder>\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n        </encoder>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>\n            <maxHistory>7</maxHistory>\n            <maxFileSize>50MB</maxFileSize>\n            <totalSizeCap>20GB</totalSizeCap>\n        </rollingPolicy>\n    </appender>\n\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{30} - %msg%n</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\" />\n    </root>\n\n    <!--应用日志-->\n    <logger name=\"com.alibaba.craftsman\" level=\"DEBUG\"/>\n\n    <!--数据库日志-->\n    <logger name=\"com.apache.ibatis\" level=\"TRACE\"/>\n    <logger name=\"java.sql.Connection\" level=\"DEBUG\"/>\n    <logger name=\"java.sql.Statement\" level=\"DEBUG\"/>\n</configuration>"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.ATAMetricAddCmdExeTest_testATAMetricAddSuccess",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\r\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\r\n---------------------------\r\n[\r\n\t1\r\n]\r\n===========================\r\n\r\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.ATAMetricAddCmdExeTest_testATAMetricAddSuccess_inputParams",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"id\":\"1073\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"commentCount\\\":14,\\\"favoriteCount\\\":49,\\\"hitCount\\\":299,\\\"thumbsUpCount\\\":89,\\\"title\\\":\\\"testATAMetricAddSuccess\\\",\\\"url\\\":\\\"sharingLink\\\"}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"ATA\",\n\t\t\t\"userId\":\"ATAMetricAddCmdExeTest1552554148803\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.CodeReviewMetricAddCmdExeTest_testSuccess",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\r\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\r\n---------------------------\r\n[\r\n\t1\r\n]\r\n===========================\r\n\r\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.CodeReviewMetricAddCmdExeTest_testSuccess_inputParams",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"id\":\"1074\",\n\t\t\t\"mainMetric\":\"tech-contribution\",\n\t\t\t\"metricItem\":\"{\\\"noteCount\\\":8,\\\"reviewDocLink\\\":\\\"http://www.alibaba.com \\\",\\\"reviewId\\\":\\\"72376263\\\"}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"CodeReview\",\n\t\t\t\"userId\":\"CodeReviewMetricAddCmdExeTest_098873\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.MetricDeleteCmdExeTest_testSuccess",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\r\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_delete\r\n---------------------------\r\n[\r\n\t0\r\n]\r\n===========================\r\n\r\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.MetricDeleteCmdExeTest_testSuccess_inputParams",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_delete\n=================method_params_separator================\n[\n\t[\n\t\t\"1013\",\n\t\t\"MetricDeleteCmdExeTest\"\n\t]\n]\n=======================different_method_separator=============================\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.MiscMetricAddCmdExeTest_testSuccess",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\r\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\r\n---------------------------\r\n[\r\n\t1\r\n]\r\n===========================\r\n\r\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.MiscMetricAddCmdExeTest_testSuccess_inputParams",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"id\":\"1075\",\n\t\t\t\"mainMetric\":\"tech-contribution\",\n\t\t\t\"metricItem\":\"{\\\"codeUrl\\\":\\\"codeUrl\\\",\\\"content\\\":\\\"Highlight content\\\",\\\"docUrl\\\":\\\"docUrl\\\",\\\"name\\\":\\\"Tech highlight\\\"}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"Misc\",\n\t\t\t\"userId\":\"MiscMetricAddCmdExeTest_09888\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.PaperMetricAddCmdExeTest_testPaperMetricAddSuccess",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\r\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\r\n---------------------------\r\n[\r\n\t1\r\n]\r\n===========================\r\n\r\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.PaperMetricAddCmdExeTest_testPaperMetricAddSuccess_inputParams",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"id\":\"1076\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"magazine\\\":\\\"IEEE\\\",\\\"paperDesc\\\":\\\"paper Description\\\",\\\"paperLink\\\":\\\"http://www.alibaba.com\\\",\\\"paperName\\\":\\\"paperName\\\"}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"Paper\",\n\t\t\t\"userId\":\"PaperMetricAddCmdExeTest_098872\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.PatentMetricAddCmdExeTest_testPatentMetricAddSuccess",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\r\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\r\n---------------------------\r\n[\r\n\t1\r\n]\r\n===========================\r\n\r\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.PatentMetricAddCmdExeTest_testPatentMetricAddSuccess_inputParams",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"jack ma\",\n\t\t\t\"id\":\"1077\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"authorType\\\":\\\"FIRST_AUTHOR\\\",\\\"patentDesc\\\":\\\"This is a very valuable patent\\\",\\\"patentName\\\":\\\"patentName\\\",\\\"patentNo\\\":\\\"73499992323\\\",\\\"patentUrl\\\":\\\"http://www.alibaba.com\\\"}\",\n\t\t\t\"modifier\":\"jack ma\",\n\t\t\t\"subMetric\":\"Patent\",\n\t\t\t\"userId\":\"PatentMetricAddCmdExeTest_627327\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.RefactoringMetricAddCmdExeTest_testSuccess",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\r\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\r\n---------------------------\r\n[\r\n\t1\r\n]\r\n===========================\r\n\r\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.RefactoringMetricAddCmdExeTest_testSuccess_inputParams",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"Lucy\",\n\t\t\t\"id\":\"1078\",\n\t\t\t\"mainMetric\":\"tech-contribution\",\n\t\t\t\"metricItem\":\"{\\\"codeUrl\\\":\\\"codeUrl\\\",\\\"content\\\":\\\"Refactor Content\\\",\\\"docUrl\\\":\\\"docUrl\\\",\\\"name\\\":\\\"Refactor Leads code\\\",\\\"refactoringLevel\\\":\\\"MODULE\\\"}\",\n\t\t\t\"modifier\":\"Lucy\",\n\t\t\t\"subMetric\":\"Refactoring\",\n\t\t\t\"userId\":\"RefactoringMetricAddCmdExeTest_09098\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.ScoreRecalculateTest_testDevSuccess",
    "content": "{\"initialized\":false,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_create\n---------------------------\n[\n\t1\n]\n===========================\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\n---------------------------\n[\n\t1,\n\t1,\n\t1\n]\n===========================\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_getByUserId\n---------------------------\n[\n\t{\n\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\"appQualityScore\":0.0D,\n\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\"creator\":\"testSuccessAdd\",\n\t\t\"dep\":\"ICBU\",\n\t\t\"devQualityScore\":0.0D,\n\t\t\"gmtCreate\":1552601089000,\n\t\t\"gmtModified\":1552601089000,\n\t\t\"id\":\"1067\",\n\t\t\"isManager\":\"y\",\n\t\t\"modifier\":\"testSuccessAdd\",\n\t\t\"role\":\"DEV\",\n\t\t\"techContributionScore\":0.0D,\n\t\t\"techInfluenceScore\":0.0D,\n\t\t\"totalScore\":0.0D,\n\t\t\"userId\":\"ScoreRecalculateTest1552554289036\",\n\t\t\"userName\":\"Frank\"\n\t},\n\t{\n\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\"appQualityScore\":79.0D,\n\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\"creator\":\"testSuccessAdd\",\n\t\t\"dep\":\"ICBU\",\n\t\t\"devQualityScore\":90.0D,\n\t\t\"gmtCreate\":1552601089000,\n\t\t\"gmtModified\":1552601089000,\n\t\t\"id\":\"1067\",\n\t\t\"isManager\":\"y\",\n\t\t\"modifier\":\"System\",\n\t\t\"role\":\"DEV\",\n\t\t\"techContributionScore\":0.0D,\n\t\t\"techInfluenceScore\":3.75D,\n\t\t\"totalScore\":34.93D,\n\t\t\"userId\":\"ScoreRecalculateTest1552554289036\",\n\t\t\"userName\":\"Frank\"\n\t},\n\t{\n\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\"appQualityScore\":79.0D,\n\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\"creator\":\"testSuccessAdd\",\n\t\t\"dep\":\"ICBU\",\n\t\t\"devQualityScore\":90.0D,\n\t\t\"gmtCreate\":1552601089000,\n\t\t\"gmtModified\":1552601089000,\n\t\t\"id\":\"1067\",\n\t\t\"isManager\":\"y\",\n\t\t\"modifier\":\"System\",\n\t\t\"role\":\"DEV\",\n\t\t\"techContributionScore\":0.0D,\n\t\t\"techInfluenceScore\":7.5D,\n\t\t\"totalScore\":36.05D,\n\t\t\"userId\":\"ScoreRecalculateTest1552554289036\",\n\t\t\"userName\":\"Frank\"\n\t}\n]\n===========================\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_listByMainMetric\n---------------------------\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"gmtCreate\":1552601089000,\n\t\t\t\"gmtModified\":1552601089000,\n\t\t\t\"id\":\"1080\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"url\\\": \\\"sharingLink\\\", \\\"title\\\": \\\"testATAMetricAddSuccess\\\", \\\"hitCount\\\": 299, \\\"commentCount\\\": 14, \\\"favoriteCount\\\": 49, \\\"thumbsUpCount\\\": 89}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"ATA\",\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\"\n\t\t}\n\t],\n\t[],\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"gmtCreate\":1552601089000,\n\t\t\t\"gmtModified\":1552601089000,\n\t\t\t\"id\":\"1080\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"url\\\": \\\"sharingLink\\\", \\\"title\\\": \\\"testATAMetricAddSuccess\\\", \\\"hitCount\\\": 299, \\\"commentCount\\\": 14, \\\"favoriteCount\\\": 49, \\\"thumbsUpCount\\\": 89}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"ATA\",\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\"\n\t\t},\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"gmtCreate\":1552601089000,\n\t\t\t\"gmtModified\":1552601089000,\n\t\t\t\"id\":\"1081\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"url\\\": \\\"sharingLink\\\", \\\"title\\\": \\\"testATAMetricAddSuccess\\\", \\\"hitCount\\\": 299, \\\"commentCount\\\": 14, \\\"favoriteCount\\\": 49, \\\"thumbsUpCount\\\": 89}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"ATA\",\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\"\n\t\t}\n\t],\n\t[],\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"gmtCreate\":1552601089000,\n\t\t\t\"gmtModified\":1552601089000,\n\t\t\t\"id\":\"1080\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"url\\\": \\\"sharingLink\\\", \\\"title\\\": \\\"testATAMetricAddSuccess\\\", \\\"hitCount\\\": 299, \\\"commentCount\\\": 14, \\\"favoriteCount\\\": 49, \\\"thumbsUpCount\\\": 89}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"ATA\",\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\"\n\t\t},\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"gmtCreate\":1552601089000,\n\t\t\t\"gmtModified\":1552601089000,\n\t\t\t\"id\":\"1081\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"url\\\": \\\"sharingLink\\\", \\\"title\\\": \\\"testATAMetricAddSuccess\\\", \\\"hitCount\\\": 299, \\\"commentCount\\\": 14, \\\"favoriteCount\\\": 49, \\\"thumbsUpCount\\\": 89}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"ATA\",\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\"\n\t\t},\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"gmtCreate\":1552601089000,\n\t\t\t\"gmtModified\":1552601089000,\n\t\t\t\"id\":\"1082\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"url\\\": \\\"sharingLink\\\", \\\"title\\\": \\\"testATAMetricAddSuccess\\\", \\\"hitCount\\\": 299, \\\"commentCount\\\": 14, \\\"favoriteCount\\\": 49, \\\"thumbsUpCount\\\": 89}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"ATA\",\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\"\n\t\t}\n\t],\n\t[]\n]\n===========================\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_update\n---------------------------\n[\n\t1,\n\t1,\n\t1\n]\n===========================\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.ScoreRecalculateTest_testDevSuccess_inputParams",
    "content": "{\"initialized\":false,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_create\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\t\"appQualityScore\":0.0D,\n\t\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\t\"creator\":\"testSuccessAdd\",\n\t\t\t\"dep\":\"ICBU\",\n\t\t\t\"devQualityScore\":0.0D,\n\t\t\t\"isManager\":\"y\",\n\t\t\t\"modifier\":\"testSuccessAdd\",\n\t\t\t\"role\":\"DEV\",\n\t\t\t\"techContributionScore\":0.0D,\n\t\t\t\"techInfluenceScore\":0.0D,\n\t\t\t\"totalScore\":0.0D,\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\",\n\t\t\t\"userName\":\"Frank\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"id\":\"1080\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"commentCount\\\":14,\\\"favoriteCount\\\":49,\\\"hitCount\\\":299,\\\"thumbsUpCount\\\":89,\\\"title\\\":\\\"testATAMetricAddSuccess\\\",\\\"url\\\":\\\"sharingLink\\\"}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"ATA\",\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\"\n\t\t}\n\t],\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"id\":\"1081\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"commentCount\\\":14,\\\"favoriteCount\\\":49,\\\"hitCount\\\":299,\\\"thumbsUpCount\\\":89,\\\"title\\\":\\\"testATAMetricAddSuccess\\\",\\\"url\\\":\\\"sharingLink\\\"}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"ATA\",\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\"\n\t\t}\n\t],\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"id\":\"1082\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"commentCount\\\":14,\\\"favoriteCount\\\":49,\\\"hitCount\\\":299,\\\"thumbsUpCount\\\":89,\\\"title\\\":\\\"testATAMetricAddSuccess\\\",\\\"url\\\":\\\"sharingLink\\\"}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"ATA\",\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_getByUserId\n=================method_params_separator================\n[\n\t[\n\t\t\"ScoreRecalculateTest1552554289036\"\n\t],\n\t[\n\t\t\"ScoreRecalculateTest1552554289036\"\n\t],\n\t[\n\t\t\"ScoreRecalculateTest1552554289036\"\n\t]\n]\n=======================different_method_separator=============================\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_listByMainMetric\n=================method_params_separator================\n[\n\t[\n\t\t\"ScoreRecalculateTest1552554289036\",\n\t\t\"tech-influence\"\n\t],\n\t[\n\t\t\"ScoreRecalculateTest1552554289036\",\n\t\t\"tech-contribution\"\n\t],\n\t[\n\t\t\"ScoreRecalculateTest1552554289036\",\n\t\t\"tech-influence\"\n\t],\n\t[\n\t\t\"ScoreRecalculateTest1552554289036\",\n\t\t\"tech-contribution\"\n\t],\n\t[\n\t\t\"ScoreRecalculateTest1552554289036\",\n\t\t\"tech-influence\"\n\t],\n\t[\n\t\t\"ScoreRecalculateTest1552554289036\",\n\t\t\"tech-contribution\"\n\t]\n]\n=======================different_method_separator=============================\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_update\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\t\"appQualityScore\":79.0D,\n\t\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\t\"dep\":\"ICBU\",\n\t\t\t\"devQualityScore\":90.0D,\n\t\t\t\"id\":\"1067\",\n\t\t\t\"isManager\":\"y\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"role\":\"DEV\",\n\t\t\t\"techContributionScore\":0.0D,\n\t\t\t\"techInfluenceScore\":3.75D,\n\t\t\t\"totalScore\":34.925D,\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\",\n\t\t\t\"userName\":\"Frank\"\n\t\t}\n\t],\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\t\"appQualityScore\":79.0D,\n\t\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\t\"dep\":\"ICBU\",\n\t\t\t\"devQualityScore\":90.0D,\n\t\t\t\"id\":\"1067\",\n\t\t\t\"isManager\":\"y\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"role\":\"DEV\",\n\t\t\t\"techContributionScore\":0.0D,\n\t\t\t\"techInfluenceScore\":7.5D,\n\t\t\t\"totalScore\":36.05D,\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\",\n\t\t\t\"userName\":\"Frank\"\n\t\t}\n\t],\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\t\"appQualityScore\":79.0D,\n\t\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\t\"dep\":\"ICBU\",\n\t\t\t\"devQualityScore\":90.0D,\n\t\t\t\"id\":\"1067\",\n\t\t\t\"isManager\":\"y\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"role\":\"DEV\",\n\t\t\t\"techContributionScore\":0.0D,\n\t\t\t\"techInfluenceScore\":11.25D,\n\t\t\t\"totalScore\":37.175D,\n\t\t\t\"userId\":\"ScoreRecalculateTest1552554289036\",\n\t\t\t\"userName\":\"Frank\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.SharingMetricAddCmdExeTest_testSharingMetricAddSuccess",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\r\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\r\n---------------------------\r\n[\r\n\t1\r\n]\r\n===========================\r\n\r\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.SharingMetricAddCmdExeTest_testSharingMetricAddSuccess_inputParams",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_create\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.MetricDO\",\n\t\t\t\"creator\":\"System\",\n\t\t\t\"id\":\"1079\",\n\t\t\t\"mainMetric\":\"tech-influence\",\n\t\t\t\"metricItem\":\"{\\\"sharingLink\\\":\\\"sharing.com\\\",\\\"sharingName\\\":\\\"Structured thinking\\\",\\\"sharingScope\\\":\\\"TEAM\\\"}\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"subMetric\":\"Sharing\",\n\t\t\t\"userId\":\"testSharingMetricAddSuccess_089765\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.UserProfileCmdExeTest_testSuccessAdd",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_create\n---------------------------\n[\n\t1\n]\n===========================\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_getByUserId\n---------------------------\n[\n\t{\n\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\"appQualityScore\":0.0D,\n\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\"creator\":\"testSuccessAdd\",\n\t\t\"dep\":\"ICBU\",\n\t\t\"devQualityScore\":0.0D,\n\t\t\"gmtCreate\":1552601073000,\n\t\t\"gmtModified\":1552601073000,\n\t\t\"id\":\"1065\",\n\t\t\"isManager\":\"y\",\n\t\t\"modifier\":\"testSuccessAdd\",\n\t\t\"role\":\"DEV\",\n\t\t\"techContributionScore\":0.0D,\n\t\t\"techInfluenceScore\":0.0D,\n\t\t\"totalScore\":0.0D,\n\t\t\"userId\":\"UserProfileCmdExeTest1000\",\n\t\t\"userName\":\"Frank\"\n\t},\n\t{\n\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\"appQualityScore\":79.0D,\n\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\"creator\":\"testSuccessAdd\",\n\t\t\"dep\":\"ICBU\",\n\t\t\"devQualityScore\":90.0D,\n\t\t\"gmtCreate\":1552601073000,\n\t\t\"gmtModified\":1552601073000,\n\t\t\"id\":\"1065\",\n\t\t\"isManager\":\"y\",\n\t\t\"modifier\":\"System\",\n\t\t\"role\":\"DEV\",\n\t\t\"techContributionScore\":0.0D,\n\t\t\"techInfluenceScore\":0.0D,\n\t\t\"totalScore\":33.8D,\n\t\t\"userId\":\"UserProfileCmdExeTest1000\",\n\t\t\"userName\":\"Frank\"\n\t}\n]\n===========================\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_listByMainMetric\n---------------------------\n[\n\t[],\n\t[]\n]\n===========================\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_update\n---------------------------\n[\n\t1\n]\n===========================\n\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.UserProfileCmdExeTest_testSuccessAdd_inputParams",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_create\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\t\"appQualityScore\":0.0D,\n\t\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\t\"creator\":\"testSuccessAdd\",\n\t\t\t\"dep\":\"ICBU\",\n\t\t\t\"devQualityScore\":0.0D,\n\t\t\t\"isManager\":\"y\",\n\t\t\t\"modifier\":\"testSuccessAdd\",\n\t\t\t\"role\":\"DEV\",\n\t\t\t\"techContributionScore\":0.0D,\n\t\t\t\"techInfluenceScore\":0.0D,\n\t\t\t\"totalScore\":0.0D,\n\t\t\t\"userId\":\"UserProfileCmdExeTest1000\",\n\t\t\t\"userName\":\"Frank\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_getByUserId\n=================method_params_separator================\n[\n\t[\n\t\t\"UserProfileCmdExeTest1000\"\n\t],\n\t[\n\t\t\"UserProfileCmdExeTest1000\"\n\t]\n]\n=======================different_method_separator=============================\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_listByMainMetric\n=================method_params_separator================\n[\n\t[\n\t\t\"UserProfileCmdExeTest1000\",\n\t\t\"tech-influence\"\n\t],\n\t[\n\t\t\"UserProfileCmdExeTest1000\",\n\t\t\"tech-contribution\"\n\t]\n]\n=======================different_method_separator=============================\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_update\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\t\"appQualityScore\":79.0D,\n\t\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\t\"dep\":\"ICBU\",\n\t\t\t\"devQualityScore\":90.0D,\n\t\t\t\"id\":\"1065\",\n\t\t\t\"isManager\":\"y\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"role\":\"DEV\",\n\t\t\t\"techContributionScore\":0.0D,\n\t\t\t\"techInfluenceScore\":0.0D,\n\t\t\t\"totalScore\":33.8D,\n\t\t\t\"userId\":\"UserProfileCmdExeTest1000\",\n\t\t\t\"userName\":\"Frank\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.UserProfileCmdExeTest_testSuccessUpdate",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_create\n---------------------------\n[\n\t1\n]\n===========================\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_getByUserId\n---------------------------\n[\n\t{\n\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\"appQualityScore\":0.0D,\n\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\"creator\":\"testSuccessAdd\",\n\t\t\"dep\":\"ICBU\",\n\t\t\"devQualityScore\":0.0D,\n\t\t\"gmtCreate\":1552601079000,\n\t\t\"gmtModified\":1552601079000,\n\t\t\"id\":\"1066\",\n\t\t\"isManager\":\"y\",\n\t\t\"modifier\":\"testSuccessAdd\",\n\t\t\"role\":\"DEV\",\n\t\t\"techContributionScore\":0.0D,\n\t\t\"techInfluenceScore\":0.0D,\n\t\t\"totalScore\":0.0D,\n\t\t\"userId\":\"UserProfileCmdExeTest1000\",\n\t\t\"userName\":\"Frank\"\n\t},\n\t{\n\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\"appQualityScore\":79.0D,\n\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\"creator\":\"testSuccessAdd\",\n\t\t\"dep\":\"ICBU\",\n\t\t\"devQualityScore\":90.0D,\n\t\t\"gmtCreate\":1552601079000,\n\t\t\"gmtModified\":1552601079000,\n\t\t\"id\":\"1066\",\n\t\t\"isManager\":\"y\",\n\t\t\"modifier\":\"System\",\n\t\t\"role\":\"DEV\",\n\t\t\"techContributionScore\":0.0D,\n\t\t\"techInfluenceScore\":0.0D,\n\t\t\"totalScore\":33.8D,\n\t\t\"userId\":\"UserProfileCmdExeTest1000\",\n\t\t\"userName\":\"Frank\"\n\t},\n\t{\n\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\"appQualityScore\":79.0D,\n\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\"creator\":\"testSuccessAdd\",\n\t\t\"dep\":\"ICBU\",\n\t\t\"devQualityScore\":90.0D,\n\t\t\"gmtCreate\":1552601079000,\n\t\t\"gmtModified\":1552601079000,\n\t\t\"id\":\"1066\",\n\t\t\"isManager\":\"y\",\n\t\t\"modifier\":\"testSuccessUpdate\",\n\t\t\"role\":\"QA\",\n\t\t\"techContributionScore\":100.0D,\n\t\t\"techInfluenceScore\":200.0D,\n\t\t\"totalScore\":33.8D,\n\t\t\"userId\":\"UserProfileCmdExeTest1000\",\n\t\t\"userName\":\"Frank\"\n\t}\n]\n===========================\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_listByMainMetric\n---------------------------\n[\n\t[],\n\t[]\n]\n===========================\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_update\n---------------------------\n[\n\t1,\n\t1\n]\n===========================\n\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/com.alibaba.craftsman.app.UserProfileCmdExeTest_testSuccessUpdate_inputParams",
    "content": "{\"initialized\":true,\"version\":\"1.0.0\"}\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_create\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\t\"appQualityScore\":0.0D,\n\t\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\t\"creator\":\"testSuccessAdd\",\n\t\t\t\"dep\":\"ICBU\",\n\t\t\t\"devQualityScore\":0.0D,\n\t\t\t\"isManager\":\"y\",\n\t\t\t\"modifier\":\"testSuccessAdd\",\n\t\t\t\"role\":\"DEV\",\n\t\t\t\"techContributionScore\":0.0D,\n\t\t\t\"techInfluenceScore\":0.0D,\n\t\t\t\"totalScore\":0.0D,\n\t\t\t\"userId\":\"UserProfileCmdExeTest1000\",\n\t\t\t\"userName\":\"Frank\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_getByUserId\n=================method_params_separator================\n[\n\t[\n\t\t\"UserProfileCmdExeTest1000\"\n\t],\n\t[\n\t\t\"UserProfileCmdExeTest1000\"\n\t],\n\t[\n\t\t\"UserProfileCmdExeTest1000\"\n\t]\n]\n=======================different_method_separator=============================\ncom.alibaba.craftsman.gatewayimpl.database.MetricTunnel_listByMainMetric\n=================method_params_separator================\n[\n\t[\n\t\t\"UserProfileCmdExeTest1000\",\n\t\t\"tech-influence\"\n\t],\n\t[\n\t\t\"UserProfileCmdExeTest1000\",\n\t\t\"tech-contribution\"\n\t]\n]\n=======================different_method_separator=============================\ncom.alibaba.craftsman.gatewayimpl.database.UserProfileTunnel_update\n=================method_params_separator================\n[\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\t\"appQualityScore\":79.0D,\n\t\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\t\"dep\":\"ICBU\",\n\t\t\t\"devQualityScore\":90.0D,\n\t\t\t\"id\":\"1066\",\n\t\t\t\"isManager\":\"y\",\n\t\t\t\"modifier\":\"System\",\n\t\t\t\"role\":\"DEV\",\n\t\t\t\"techContributionScore\":0.0D,\n\t\t\t\"techInfluenceScore\":0.0D,\n\t\t\t\"totalScore\":33.8D,\n\t\t\t\"userId\":\"UserProfileCmdExeTest1000\",\n\t\t\t\"userName\":\"Frank\"\n\t\t}\n\t],\n\t[\n\t\t{\n\t\t\t\"@type\":\"com.alibaba.craftsman.gatewayimpl.database.dataobject.UserProfileDO\",\n\t\t\t\"appQualityScore\":0.0D,\n\t\t\t\"checkinCodeQuantity\":0.0D,\n\t\t\t\"devQualityScore\":0.0D,\n\t\t\t\"modifier\":\"testSuccessUpdate\",\n\t\t\t\"role\":\"QA\",\n\t\t\t\"techContributionScore\":100.0D,\n\t\t\t\"techInfluenceScore\":200.0D,\n\t\t\t\"totalScore\":0.0D,\n\t\t\t\"userId\":\"UserProfileCmdExeTest1000\"\n\t\t}\n\t]\n]\n=======================different_method_separator=============================\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/mockfile/service.list",
    "content": "ATAMetricAddCmdExe_@_com.alibaba.craftsman.command.ATAMetricAddCmdExe\r\nATAMetricQryExe_@_com.alibaba.craftsman.command.query.ATAMetricQryExe\r\nappMetricTunnel_@_com.alibaba.craftsman.tunnel.rpc.AppMetricTunnel\r\napplicationContextHelper_@_com.alibaba.cola.common.ApplicationContextHelper\r\napplicationTaskExecutor_@_org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor\r\napplication_@_com.alibaba.craftsman.Application\r\nbasicErrorController_@_org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController\r\nbeanNameHandlerMapping_@_org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping\r\nbeanNameViewResolver_@_org.springframework.web.servlet.view.BeanNameViewResolver\r\nbootstrap_@_com.alibaba.cola.boot.SpringBootstrap\r\nbugMetricTunnel_@_com.alibaba.craftsman.tunnel.rpc.BugMetricTunnel\r\ncharacterEncodingFilter_@_org.springframework.web.filter.CharacterEncodingFilter\r\ncodeReviewMetricAddCmdExe_@_com.alibaba.craftsman.command.CodeReviewMetricAddCmdExe\r\ncolaConfig_@_com.alibaba.craftsman.config.ColaConfig\r\nconventionErrorViewResolver_@_org.springframework.boot.autoconfigure.web.servlet.error.DefaultErrorViewResolver\r\ndataSource_@_com.zaxxer.hikari.HikariDataSource\r\ndefaultServletHandlerMapping_@_org.springframework.web.servlet.HandlerMapping\r\ndefaultValidator_@_org.springframework.validation.beanvalidation.LocalValidatorFactoryBean\r\ndefaultViewResolver_@_org.springframework.web.servlet.view.InternalResourceViewResolver\r\ndispatcherServletRegistration_@_org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean\r\ndispatcherServlet_@_org.springframework.web.servlet.DispatcherServlet\r\ndomainEventPublisher_@_com.alibaba.craftsman.common.util.DomainEventPublisher\r\nerrorAttributes_@_org.springframework.boot.web.servlet.error.DefaultErrorAttributes\r\nerrorPageCustomizer_@_org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$ErrorPageCustomizer\r\nerror_@_org.springframework.web.servlet.View\r\neventBus_@_com.alibaba.cola.event.EventBus\r\neventHub_@_com.alibaba.cola.event.EventHub\r\neventRegister_@_com.alibaba.cola.boot.EventRegister\r\nextensionExecutor_@_com.alibaba.cola.extension.ExtensionExecutor\r\nextensionRegister_@_com.alibaba.cola.boot.ExtensionRegister\r\nextensionRepository_@_com.alibaba.cola.extension.ExtensionRepository\r\nfaviconHandlerMapping_@_org.springframework.web.servlet.handler.SimpleUrlHandlerMapping\r\nfaviconRequestHandler_@_org.springframework.web.servlet.resource.ResourceHttpRequestHandler\r\nformContentFilter_@_org.springframework.boot.web.servlet.filter.OrderedFormContentFilter\r\ngroovyMarkupConfigurer_@_org.springframework.web.servlet.view.groovy.GroovyMarkupConfigurer\r\ngroovyMarkupViewResolver_@_org.springframework.web.servlet.view.groovy.GroovyMarkupViewResolver\r\ngsonBuilder_@_com.google.gson.GsonBuilder\r\ngson_@_com.google.gson.Gson\r\nhandlerExceptionResolver_@_org.springframework.web.servlet.HandlerExceptionResolver\r\nhiddenHttpMethodFilter_@_org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter\r\nhikariPoolDataSourceMetadataProvider_@_org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider\r\nhttpRequestHandlerAdapter_@_org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter\r\njacksonCodecCustomizer_@_org.springframework.boot.web.codec.CodecCustomizer\r\njacksonObjectMapperBuilder_@_org.springframework.http.converter.json.Jackson2ObjectMapperBuilder\r\njacksonObjectMapper_@_com.fasterxml.jackson.databind.ObjectMapper\r\njdbcTemplate_@_org.springframework.jdbc.core.JdbcTemplate\r\njsonComponentModule_@_org.springframework.boot.jackson.JsonComponentModule\r\nlocaleCharsetMappingsCustomizer_@_org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration$LocaleCharsetMappingsCustomizer\r\nloggingCodecCustomizer_@_org.springframework.boot.web.codec.CodecCustomizer\r\nmappingJackson2HttpMessageConverter_@_org.springframework.http.converter.json.MappingJackson2HttpMessageConverter\r\nmbeanExporter_@_org.springframework.jmx.export.annotation.AnnotationMBeanExporter\r\nmbeanServer_@_javax.management.MBeanServer\r\nmessageConverters_@_org.springframework.boot.autoconfigure.http.HttpMessageConverters\r\nmetricDeleteCmdExe_@_com.alibaba.craftsman.command.MetricDeleteCmdExe\r\nmetricItemCreatedHandler_@_com.alibaba.craftsman.event.handler.MetricItemCreatedHandler\r\nmetricRepository_@_com.alibaba.craftsman.repository.MetricRepository\r\nmetricTunnel_@_com.alibaba.craftsman.tunnel.database.MetricTunnel\r\nmetricsController_@_com.alibaba.craftsman.controller.MetricsController\r\nmetricsServiceImpl_@_com.alibaba.craftsman.service.MetricsServiceImpl\r\nmiscMetricAddCmdExe_@_com.alibaba.craftsman.command.MiscMetricAddCmdExe\r\nmultipartConfigElement_@_javax.servlet.MultipartConfigElement\r\nmultipartResolver_@_org.springframework.web.multipart.support.StandardServletMultipartResolver\r\nmvcContentNegotiationManager_@_org.springframework.web.accept.ContentNegotiationManager\r\nmvcConversionService_@_org.springframework.format.support.FormattingConversionService\r\nmvcHandlerMappingIntrospector_@_org.springframework.web.servlet.handler.HandlerMappingIntrospector\r\nmvcPathMatcher_@_org.springframework.util.PathMatcher\r\nmvcResourceUrlProvider_@_org.springframework.web.servlet.resource.ResourceUrlProvider\r\nmvcUriComponentsContributor_@_org.springframework.web.method.support.CompositeUriComponentsContributor\r\nmvcUrlPathHelper_@_org.springframework.web.util.UrlPathHelper\r\nmvcValidator_@_org.springframework.validation.Validator\r\nmvcViewResolver_@_org.springframework.web.servlet.ViewResolver\r\nmybatis-org.mybatis.spring.boot.autoconfigure.MybatisProperties_@_org.mybatis.spring.boot.autoconfigure.MybatisProperties\r\nnamedParameterJdbcTemplate_@_org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate\r\nobjectNamingStrategy_@_org.springframework.boot.autoconfigure.jmx.ParentAwareNamingStrategy\r\norg.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration_@_org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration\r\norg.springframework.boot.autoconfigure.AutoConfigurationPackages_@_org.springframework.boot.autoconfigure.AutoConfigurationPackages$BasePackages\r\norg.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration_@_org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration\r\norg.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration_@_org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration\r\norg.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration_@_org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration\r\norg.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration$GroovyMarkupConfiguration_@_org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration$GroovyMarkupConfiguration\r\norg.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration$GroovyWebConfiguration_@_org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration$GroovyWebConfiguration\r\norg.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration_@_org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration\r\norg.springframework.boot.autoconfigure.gson.GsonAutoConfiguration_@_org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration\r\norg.springframework.boot.autoconfigure.http.GsonHttpMessageConvertersConfiguration_@_org.springframework.boot.autoconfigure.http.GsonHttpMessageConvertersConfiguration\r\norg.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration_@_org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration\r\norg.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration_@_org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration\r\norg.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration_@_org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration\r\norg.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration_@_org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration\r\norg.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration$JacksonCodecConfiguration_@_org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration$JacksonCodecConfiguration\r\norg.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration$LoggingCodecConfiguration_@_org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration$LoggingCodecConfiguration\r\norg.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration_@_org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration\r\norg.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration_@_org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration\r\norg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration_@_org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration\r\norg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration_@_org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration\r\norg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration_@_org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration\r\norg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfiguration_@_org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfiguration\r\norg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration_@_org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration\r\norg.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceConfiguration_@_org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceConfiguration\r\norg.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration_@_org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration\r\norg.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari_@_org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari\r\norg.springframework.boot.autoconfigure.jdbc.DataSourceInitializationConfiguration_@_org.springframework.boot.autoconfigure.jdbc.DataSourceInitializationConfiguration\r\norg.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker_@_org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker\r\norg.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration$Hikari_@_org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration$Hikari\r\norg.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration_@_org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration\r\norg.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration$DataSourceTransactionManagerConfiguration_@_org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration$DataSourceTransactionManagerConfiguration\r\norg.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration_@_org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration\r\norg.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration$JdbcTemplateConfiguration_@_org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration$JdbcTemplateConfiguration\r\norg.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration$NamedParameterJdbcTemplateConfiguration_@_org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration$NamedParameterJdbcTemplateConfiguration\r\norg.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration_@_org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration\r\norg.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration_@_org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration\r\norg.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration_@_org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration\r\norg.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration_@_org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration\r\norg.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration_@_org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration\r\norg.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration_@_org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration\r\norg.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration$CglibAutoProxyConfiguration_@_org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration$CglibAutoProxyConfiguration\r\norg.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration_@_org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration\r\norg.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$TransactionTemplateConfiguration_@_org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$TransactionTemplateConfiguration\r\norg.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration_@_org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration\r\norg.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration_@_org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration\r\norg.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration_@_org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration\r\norg.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration_@_org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration\r\norg.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration_@_org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration\r\norg.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration_@_org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration\r\norg.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration_@_org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration\r\norg.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration_@_org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration\r\norg.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration_@_org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration\r\norg.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration_@_org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration\r\norg.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration_@_org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration\r\norg.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat_@_org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat\r\norg.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration_@_org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration\r\norg.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter$FaviconConfiguration_@_org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter$FaviconConfiguration\r\norg.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter_@_org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter\r\norg.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration_@_org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration\r\norg.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration_@_org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration\r\norg.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration_@_org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration\r\norg.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration_@_org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration\r\norg.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration_@_org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration\r\norg.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration_@_org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration\r\norg.springframework.transaction.annotation.ProxyTransactionManagementConfiguration_@_org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration\r\norg.springframework.transaction.config.internalTransactionAdvisor_@_org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor\r\npaperMetricAddCmdExe_@_com.alibaba.craftsman.command.PaperMetricAddCmdExe\r\nparameterNamesModule_@_com.fasterxml.jackson.module.paramnames.ParameterNamesModule\r\npatentMetricAddCmdExe_@_com.alibaba.craftsman.command.PatentMetricAddCmdExe\r\nplatformTransactionManagerCustomizers_@_org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers\r\nrefactoringMetricAddCmdExe_@_com.alibaba.craftsman.command.RefactoringMetricAddCmdExe\r\nrefreshScoreCmdExe_@_com.alibaba.craftsman.command.RefreshScoreCmdExe\r\nrequestContextFilter_@_org.springframework.web.filter.RequestContextFilter\r\nrequestMappingHandlerAdapter_@_org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter\r\nrequestMappingHandlerMapping_@_org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping\r\nresourceHandlerMapping_@_org.springframework.web.servlet.HandlerMapping\r\nrestTemplateBuilder_@_org.springframework.boot.web.client.RestTemplateBuilder\r\nserver-org.springframework.boot.autoconfigure.web.ServerProperties_@_org.springframework.boot.autoconfigure.web.ServerProperties\r\nservletWebServerFactoryCustomizer_@_org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryCustomizer\r\nsharingMetricAddCmdExe_@_com.alibaba.craftsman.command.SharingMetricAddCmdExe\r\nsimpleControllerHandlerAdapter_@_org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter\r\nspring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties_@_org.springframework.boot.autoconfigure.jdbc.DataSourceProperties\r\nspring.groovy.template-org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateProperties_@_org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateProperties\r\nspring.gson-org.springframework.boot.autoconfigure.gson.GsonProperties_@_org.springframework.boot.autoconfigure.gson.GsonProperties\r\nspring.http-org.springframework.boot.autoconfigure.http.HttpProperties_@_org.springframework.boot.autoconfigure.http.HttpProperties\r\nspring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties_@_org.springframework.boot.autoconfigure.info.ProjectInfoProperties\r\nspring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties_@_org.springframework.boot.autoconfigure.jackson.JacksonProperties\r\nspring.jdbc-org.springframework.boot.autoconfigure.jdbc.JdbcProperties_@_org.springframework.boot.autoconfigure.jdbc.JdbcProperties\r\nspring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties_@_org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties\r\nspring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties_@_org.springframework.boot.autoconfigure.web.ResourceProperties\r\nspring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties_@_org.springframework.boot.autoconfigure.web.servlet.MultipartProperties\r\nspring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties_@_org.springframework.boot.autoconfigure.task.TaskExecutionProperties\r\nspring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties_@_org.springframework.boot.autoconfigure.task.TaskSchedulingProperties\r\nspring.transaction-org.springframework.boot.autoconfigure.transaction.TransactionProperties_@_org.springframework.boot.autoconfigure.transaction.TransactionProperties\r\nsqlSessionFactory_@_org.apache.ibatis.session.SqlSessionFactory\r\nsqlSessionTemplate_@_org.mybatis.spring.SqlSessionTemplate\r\nstandardGsonBuilderCustomizer_@_org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration$StandardGsonBuilderCustomizer\r\nstandardJacksonObjectMapperBuilderCustomizer_@_org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration$StandardJackson2ObjectMapperBuilderCustomizer\r\nstringHttpMessageConverter_@_org.springframework.http.converter.StringHttpMessageConverter\r\ntaskExecutorBuilder_@_org.springframework.boot.task.TaskExecutorBuilder\r\ntaskSchedulerBuilder_@_org.springframework.boot.task.TaskSchedulerBuilder\r\ntestsContainer_@_com.alibaba.cola.container.TestsContainer\r\ntomcatServletWebServerFactoryCustomizer_@_org.springframework.boot.autoconfigure.web.servlet.TomcatServletWebServerFactoryCustomizer\r\ntomcatServletWebServerFactory_@_org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory\r\ntomcatWebServerFactoryCustomizer_@_org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer\r\ntransactionAttributeSource_@_org.springframework.transaction.interceptor.TransactionAttributeSource\r\ntransactionInterceptor_@_org.springframework.transaction.interceptor.TransactionInterceptor\r\ntransactionManager_@_org.springframework.jdbc.datasource.DataSourceTransactionManager\r\ntransactionTemplate_@_org.springframework.transaction.support.TransactionTemplate\r\nuserProfileAddCmdExe_@_com.alibaba.craftsman.command.UserProfileAddCmdExe\r\nuserProfileGetQryExe_@_com.alibaba.craftsman.command.query.UserProfileGetQryExe\r\nuserProfileListQryExe_@_com.alibaba.craftsman.command.query.UserProfileListQryExe\r\nuserProfileRepository_@_com.alibaba.craftsman.repository.UserProfileRepository\r\nuserProfileServiceImpl_@_com.alibaba.craftsman.service.UserProfileServiceImpl\r\nuserProfileTunnel_@_com.alibaba.craftsman.tunnel.database.UserProfileTunnel\r\nuserProfileUpdateCmdExe_@_com.alibaba.craftsman.command.UserProfileUpdateCmdExe\r\nviewControllerHandlerMapping_@_org.springframework.web.servlet.HandlerMapping\r\nviewResolver_@_org.springframework.web.servlet.view.ContentNegotiatingViewResolver\r\nwebsocketServletWebServerCustomizer_@_org.springframework.boot.autoconfigure.websocket.servlet.TomcatWebSocketServletWebServerCustomizer\r\nwelcomePageHandlerMapping_@_org.springframework.boot.autoconfigure.web.servlet.WelcomePageHandlerMapping\r\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/resources/spring-mock-test.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:context=\"http://www.springframework.org/schema/context\" xmlns:cola=\"http://repo.alibaba-inc.com/schema/cola\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n\thttp://www.springframework.org/schema/beans/spring-beans-3.1.xsd\n\thttp://repo.alibaba-inc.com/schema/cola http://repo.alibaba-inc.com/schema/cola-mock.xsd\n\thttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\">\n\n    <!--<context:property-placeholder location=\"classpath:test.properties\"/>-->\n    <context:component-scan base-package=\"com.alibaba.craftsman, com.alibaba.cola\" >\n        <!-- 排除Application和TestApplication-->\n        <context:exclude-filter type=\"annotation\" expression=\"org.springframework.boot.autoconfigure.SpringBootApplication\"/>\n        <!-- 排除Dao-->\n        <context:exclude-filter type=\"annotation\" expression=\"org.apache.ibatis.annotations.Mapper\"/>\n    </context:component-scan>\n\n    <cola:cola-mock base-package=\"com.alibaba.craftsman\" />\n</beans>"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/testAddCmd.http",
    "content": "POST http://localhost:8080/metrics/ata\nContent-Type: application/json\nCache-Control: no-cache\n\n{\n    \"ataMetricCO\": {\n        \"ownerId\": 12345,\n        \"title\": \"testAdd\",\n        \"url\": \"testAdd\",\n        \"thumbsUpCount\": 100,\n        \"hitCount\": 10,\n        \"commentCount\": 10,\n        \"favoriteCount\": 10\n    },\n    \"needsOperator\": true,\n    \"operater\": \"system\"\n\n\n}\n"
  },
  {
    "path": "cola-samples/craftsman/start/src/test/testQry.http",
    "content": "GET http://localhost:8080/metrics/ata?ownerId=12345\nContent-Type: application/json\nCache-Control: no-cache\n"
  },
  {
    "path": "mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Apache Maven Wrapper startup batch script, version 3.3.2\n#\n# Optional ENV vars\n# -----------------\n#   JAVA_HOME - location of a JDK home dir, required when download maven via java source\n#   MVNW_REPOURL - repo url base for downloading maven distribution\n#   MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven\n#   MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output\n# ----------------------------------------------------------------------------\n\nset -euf\n[ \"${MVNW_VERBOSE-}\" != debug ] || set -x\n\n# OS specific support.\nnative_path() { printf %s\\\\n \"$1\"; }\ncase \"$(uname)\" in\nCYGWIN* | MINGW*)\n  [ -z \"${JAVA_HOME-}\" ] || JAVA_HOME=\"$(cygpath --unix \"$JAVA_HOME\")\"\n  native_path() { cygpath --path --windows \"$1\"; }\n  ;;\nesac\n\n# set JAVACMD and JAVACCMD\nset_java_home() {\n  # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched\n  if [ -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      JAVACCMD=\"$JAVA_HOME/jre/sh/javac\"\n    else\n      JAVACMD=\"$JAVA_HOME/bin/java\"\n      JAVACCMD=\"$JAVA_HOME/bin/javac\"\n\n      if [ ! -x \"$JAVACMD\" ] || [ ! -x \"$JAVACCMD\" ]; then\n        echo \"The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run.\" >&2\n        echo \"JAVA_HOME is set to \\\"$JAVA_HOME\\\", but \\\"\\$JAVA_HOME/bin/java\\\" or \\\"\\$JAVA_HOME/bin/javac\\\" does not exist.\" >&2\n        return 1\n      fi\n    fi\n  else\n    JAVACMD=\"$(\n      'set' +e\n      'unset' -f command 2>/dev/null\n      'command' -v java\n    )\" || :\n    JAVACCMD=\"$(\n      'set' +e\n      'unset' -f command 2>/dev/null\n      'command' -v javac\n    )\" || :\n\n    if [ ! -x \"${JAVACMD-}\" ] || [ ! -x \"${JAVACCMD-}\" ]; then\n      echo \"The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run.\" >&2\n      return 1\n    fi\n  fi\n}\n\n# hash string like Java String::hashCode\nhash_string() {\n  str=\"${1:-}\" h=0\n  while [ -n \"$str\" ]; do\n    char=\"${str%\"${str#?}\"}\"\n    h=$(((h * 31 + $(LC_CTYPE=C printf %d \"'$char\")) % 4294967296))\n    str=\"${str#?}\"\n  done\n  printf %x\\\\n $h\n}\n\nverbose() { :; }\n[ \"${MVNW_VERBOSE-}\" != true ] || verbose() { printf %s\\\\n \"${1-}\"; }\n\ndie() {\n  printf %s\\\\n \"$1\" >&2\n  exit 1\n}\n\ntrim() {\n  # MWRAPPER-139:\n  #   Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.\n  #   Needed for removing poorly interpreted newline sequences when running in more\n  #   exotic environments such as mingw bash on Windows.\n  printf \"%s\" \"${1}\" | tr -d '[:space:]'\n}\n\n# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties\nwhile IFS=\"=\" read -r key value; do\n  case \"${key-}\" in\n  distributionUrl) distributionUrl=$(trim \"${value-}\") ;;\n  distributionSha256Sum) distributionSha256Sum=$(trim \"${value-}\") ;;\n  esac\ndone <\"${0%/*}/.mvn/wrapper/maven-wrapper.properties\"\n[ -n \"${distributionUrl-}\" ] || die \"cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties\"\n\ncase \"${distributionUrl##*/}\" in\nmaven-mvnd-*bin.*)\n  MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/\n  case \"${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)\" in\n  *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;\n  :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;\n  :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;\n  :Linux*x86_64*) distributionPlatform=linux-amd64 ;;\n  *)\n    echo \"Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version\" >&2\n    distributionPlatform=linux-amd64\n    ;;\n  esac\n  distributionUrl=\"${distributionUrl%-bin.*}-$distributionPlatform.zip\"\n  ;;\nmaven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;\n*) MVN_CMD=\"mvn${0##*/mvnw}\" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;\nesac\n\n# apply MVNW_REPOURL and calculate MAVEN_HOME\n# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>\n[ -z \"${MVNW_REPOURL-}\" ] || distributionUrl=\"$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*\"$_MVNW_REPO_PATTERN\"}\"\ndistributionUrlName=\"${distributionUrl##*/}\"\ndistributionUrlNameMain=\"${distributionUrlName%.*}\"\ndistributionUrlNameMain=\"${distributionUrlNameMain%-bin}\"\nMAVEN_USER_HOME=\"${MAVEN_USER_HOME:-${HOME}/.m2}\"\nMAVEN_HOME=\"${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string \"$distributionUrl\")\"\n\nexec_maven() {\n  unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :\n  exec \"$MAVEN_HOME/bin/$MVN_CMD\" \"$@\" || die \"cannot exec $MAVEN_HOME/bin/$MVN_CMD\"\n}\n\nif [ -d \"$MAVEN_HOME\" ]; then\n  verbose \"found existing MAVEN_HOME at $MAVEN_HOME\"\n  exec_maven \"$@\"\nfi\n\ncase \"${distributionUrl-}\" in\n*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;\n*) die \"distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'\" ;;\nesac\n\n# prepare tmp dir\nif TMP_DOWNLOAD_DIR=\"$(mktemp -d)\" && [ -d \"$TMP_DOWNLOAD_DIR\" ]; then\n  clean() { rm -rf -- \"$TMP_DOWNLOAD_DIR\"; }\n  trap clean HUP INT TERM EXIT\nelse\n  die \"cannot create temp dir\"\nfi\n\nmkdir -p -- \"${MAVEN_HOME%/*}\"\n\n# Download and Install Apache Maven\nverbose \"Couldn't find MAVEN_HOME, downloading and installing it ...\"\nverbose \"Downloading from: $distributionUrl\"\nverbose \"Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName\"\n\n# select .zip or .tar.gz\nif ! command -v unzip >/dev/null; then\n  distributionUrl=\"${distributionUrl%.zip}.tar.gz\"\n  distributionUrlName=\"${distributionUrl##*/}\"\nfi\n\n# verbose opt\n__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''\n[ \"${MVNW_VERBOSE-}\" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v\n\n# normalize http auth\ncase \"${MVNW_PASSWORD:+has-password}\" in\n'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;\nhas-password) [ -n \"${MVNW_USERNAME-}\" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;\nesac\n\nif [ -z \"${MVNW_USERNAME-}\" ] && command -v wget >/dev/null; then\n  verbose \"Found wget ... using wget\"\n  wget ${__MVNW_QUIET_WGET:+\"$__MVNW_QUIET_WGET\"} \"$distributionUrl\" -O \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" || die \"wget: Failed to fetch $distributionUrl\"\nelif [ -z \"${MVNW_USERNAME-}\" ] && command -v curl >/dev/null; then\n  verbose \"Found curl ... using curl\"\n  curl ${__MVNW_QUIET_CURL:+\"$__MVNW_QUIET_CURL\"} -f -L -o \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" \"$distributionUrl\" || die \"curl: Failed to fetch $distributionUrl\"\nelif set_java_home; then\n  verbose \"Falling back to use Java to download\"\n  javaSource=\"$TMP_DOWNLOAD_DIR/Downloader.java\"\n  targetZip=\"$TMP_DOWNLOAD_DIR/$distributionUrlName\"\n  cat >\"$javaSource\" <<-END\n\tpublic class Downloader extends java.net.Authenticator\n\t{\n\t  protected java.net.PasswordAuthentication getPasswordAuthentication()\n\t  {\n\t    return new java.net.PasswordAuthentication( System.getenv( \"MVNW_USERNAME\" ), System.getenv( \"MVNW_PASSWORD\" ).toCharArray() );\n\t  }\n\t  public static void main( String[] args ) throws Exception\n\t  {\n\t    setDefault( new Downloader() );\n\t    java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );\n\t  }\n\t}\n\tEND\n  # For Cygwin/MinGW, switch paths to Windows format before running javac and java\n  verbose \" - Compiling Downloader.java ...\"\n  \"$(native_path \"$JAVACCMD\")\" \"$(native_path \"$javaSource\")\" || die \"Failed to compile Downloader.java\"\n  verbose \" - Running Downloader.java ...\"\n  \"$(native_path \"$JAVACMD\")\" -cp \"$(native_path \"$TMP_DOWNLOAD_DIR\")\" Downloader \"$distributionUrl\" \"$(native_path \"$targetZip\")\"\nfi\n\n# If specified, validate the SHA-256 sum of the Maven distribution zip file\nif [ -n \"${distributionSha256Sum-}\" ]; then\n  distributionSha256Result=false\n  if [ \"$MVN_CMD\" = mvnd.sh ]; then\n    echo \"Checksum validation is not supported for maven-mvnd.\" >&2\n    echo \"Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties.\" >&2\n    exit 1\n  elif command -v sha256sum >/dev/null; then\n    if echo \"$distributionSha256Sum  $TMP_DOWNLOAD_DIR/$distributionUrlName\" | sha256sum -c >/dev/null 2>&1; then\n      distributionSha256Result=true\n    fi\n  elif command -v shasum >/dev/null; then\n    if echo \"$distributionSha256Sum  $TMP_DOWNLOAD_DIR/$distributionUrlName\" | shasum -a 256 -c >/dev/null 2>&1; then\n      distributionSha256Result=true\n    fi\n  else\n    echo \"Checksum validation was requested but neither 'sha256sum' or 'shasum' are available.\" >&2\n    echo \"Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties.\" >&2\n    exit 1\n  fi\n  if [ $distributionSha256Result = false ]; then\n    echo \"Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised.\" >&2\n    echo \"If you updated your Maven version, you need to update the specified distributionSha256Sum property.\" >&2\n    exit 1\n  fi\nfi\n\n# unzip and move\nif command -v unzip >/dev/null; then\n  unzip ${__MVNW_QUIET_UNZIP:+\"$__MVNW_QUIET_UNZIP\"} \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" -d \"$TMP_DOWNLOAD_DIR\" || die \"failed to unzip\"\nelse\n  tar xzf${__MVNW_QUIET_TAR:+\"$__MVNW_QUIET_TAR\"} \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" -C \"$TMP_DOWNLOAD_DIR\" || die \"failed to untar\"\nfi\nprintf %s\\\\n \"$distributionUrl\" >\"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url\"\nmv -- \"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain\" \"$MAVEN_HOME\" || [ -d \"$MAVEN_HOME\" ] || die \"fail to move MAVEN_HOME\"\n\nclean || :\nexec_maven \"$@\"\n"
  },
  {
    "path": "mvnw.cmd",
    "content": "<# : batch portion\n@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software Foundation (ASF) under one\n@REM or more contributor license agreements.  See the NOTICE file\n@REM distributed with this work for additional information\n@REM regarding copyright ownership.  The ASF licenses this file\n@REM to you under the Apache License, Version 2.0 (the\n@REM \"License\"); you may not use this file except in compliance\n@REM with the License.  You may obtain a copy of the License at\n@REM\n@REM    http://www.apache.org/licenses/LICENSE-2.0\n@REM\n@REM Unless required by applicable law or agreed to in writing,\n@REM software distributed under the License is distributed on an\n@REM \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n@REM KIND, either express or implied.  See the License for the\n@REM specific language governing permissions and limitations\n@REM under the License.\n@REM ----------------------------------------------------------------------------\n\n@REM ----------------------------------------------------------------------------\n@REM Apache Maven Wrapper startup batch script, version 3.3.2\n@REM\n@REM Optional ENV vars\n@REM   MVNW_REPOURL - repo url base for downloading maven distribution\n@REM   MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven\n@REM   MVNW_VERBOSE - true: enable verbose log; others: silence the output\n@REM ----------------------------------------------------------------------------\n\n@IF \"%__MVNW_ARG0_NAME__%\"==\"\" (SET __MVNW_ARG0_NAME__=%~nx0)\n@SET __MVNW_CMD__=\n@SET __MVNW_ERROR__=\n@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%\n@SET PSModulePath=\n@FOR /F \"usebackq tokens=1* delims==\" %%A IN (`powershell -noprofile \"& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}\"`) DO @(\n  IF \"%%A\"==\"MVN_CMD\" (set __MVNW_CMD__=%%B) ELSE IF \"%%B\"==\"\" (echo %%A) ELSE (echo %%A=%%B)\n)\n@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%\n@SET __MVNW_PSMODULEP_SAVE=\n@SET __MVNW_ARG0_NAME__=\n@SET MVNW_USERNAME=\n@SET MVNW_PASSWORD=\n@IF NOT \"%__MVNW_CMD__%\"==\"\" (%__MVNW_CMD__% %*)\n@echo Cannot start maven from wrapper >&2 && exit /b 1\n@GOTO :EOF\n: end batch / begin powershell #>\n\n$ErrorActionPreference = \"Stop\"\nif ($env:MVNW_VERBOSE -eq \"true\") {\n  $VerbosePreference = \"Continue\"\n}\n\n# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties\n$distributionUrl = (Get-Content -Raw \"$scriptDir/.mvn/wrapper/maven-wrapper.properties\" | ConvertFrom-StringData).distributionUrl\nif (!$distributionUrl) {\n  Write-Error \"cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties\"\n}\n\nswitch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {\n  \"maven-mvnd-*\" {\n    $USE_MVND = $true\n    $distributionUrl = $distributionUrl -replace '-bin\\.[^.]*$',\"-windows-amd64.zip\"\n    $MVN_CMD = \"mvnd.cmd\"\n    break\n  }\n  default {\n    $USE_MVND = $false\n    $MVN_CMD = $script -replace '^mvnw','mvn'\n    break\n  }\n}\n\n# apply MVNW_REPOURL and calculate MAVEN_HOME\n# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>\nif ($env:MVNW_REPOURL) {\n  $MVNW_REPO_PATTERN = if ($USE_MVND) { \"/org/apache/maven/\" } else { \"/maven/mvnd/\" }\n  $distributionUrl = \"$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')\"\n}\n$distributionUrlName = $distributionUrl -replace '^.*/',''\n$distributionUrlNameMain = $distributionUrlName -replace '\\.[^.]*$','' -replace '-bin$',''\n$MAVEN_HOME_PARENT = \"$HOME/.m2/wrapper/dists/$distributionUrlNameMain\"\nif ($env:MAVEN_USER_HOME) {\n  $MAVEN_HOME_PARENT = \"$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain\"\n}\n$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString(\"x2\")}) -join ''\n$MAVEN_HOME = \"$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME\"\n\nif (Test-Path -Path \"$MAVEN_HOME\" -PathType Container) {\n  Write-Verbose \"found existing MAVEN_HOME at $MAVEN_HOME\"\n  Write-Output \"MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD\"\n  exit $?\n}\n\nif (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {\n  Write-Error \"distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl\"\n}\n\n# prepare tmp dir\n$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile\n$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path \"$TMP_DOWNLOAD_DIR_HOLDER.dir\"\n$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null\ntrap {\n  if ($TMP_DOWNLOAD_DIR.Exists) {\n    try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }\n    catch { Write-Warning \"Cannot remove $TMP_DOWNLOAD_DIR\" }\n  }\n}\n\nNew-Item -Itemtype Directory -Path \"$MAVEN_HOME_PARENT\" -Force | Out-Null\n\n# Download and Install Apache Maven\nWrite-Verbose \"Couldn't find MAVEN_HOME, downloading and installing it ...\"\nWrite-Verbose \"Downloading from: $distributionUrl\"\nWrite-Verbose \"Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName\"\n\n$webclient = New-Object System.Net.WebClient\nif ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {\n  $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)\n}\n[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n$webclient.DownloadFile($distributionUrl, \"$TMP_DOWNLOAD_DIR/$distributionUrlName\") | Out-Null\n\n# If specified, validate the SHA-256 sum of the Maven distribution zip file\n$distributionSha256Sum = (Get-Content -Raw \"$scriptDir/.mvn/wrapper/maven-wrapper.properties\" | ConvertFrom-StringData).distributionSha256Sum\nif ($distributionSha256Sum) {\n  if ($USE_MVND) {\n    Write-Error \"Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties.\"\n  }\n  Import-Module $PSHOME\\Modules\\Microsoft.PowerShell.Utility -Function Get-FileHash\n  if ((Get-FileHash \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {\n    Write-Error \"Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property.\"\n  }\n}\n\n# unzip and move\nExpand-Archive \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" -DestinationPath \"$TMP_DOWNLOAD_DIR\" | Out-Null\nRename-Item -Path \"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain\" -NewName $MAVEN_HOME_NAME | Out-Null\ntry {\n  Move-Item -Path \"$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME\" -Destination $MAVEN_HOME_PARENT | Out-Null\n} catch {\n  if (! (Test-Path -Path \"$MAVEN_HOME\" -PathType Container)) {\n    Write-Error \"fail to move MAVEN_HOME\"\n  }\n} finally {\n  try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }\n  catch { Write-Warning \"Cannot remove $TMP_DOWNLOAD_DIR\" }\n}\n\nWrite-Output \"MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD\"\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<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/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <!--\n        a dummy aggregation parent pom for development.\n\n        simplify the development process for all cola modules including examples modules, e.g.\n        - maven test/install\n        - import to IDE\n    -->\n    <groupId>com.alibaba.cola</groupId>\n    <artifactId>cola-dummy-aggregation-parent</artifactId>\n    <version>dummy-SNAPSHOT</version>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>cola-components</module>\n        <module>cola-archetypes</module>\n\n        <module>cola-samples/craftsman</module>\n        <!-- A test failed module is better to get out. -->\n        <!--\n        <module>cola-samples/charge</module>\n        -->\n    </modules>\n</project>\n"
  },
  {
    "path": "scripts/bump_cola_version",
    "content": "#!/bin/bash\nset -eEuo pipefail\n# adjust current dir to script dir\ncd \"$(dirname \"$(readlink -f \"$0\")\")\"\n\nsource bash-buddy/lib/trap_error_info.sh\nsource bash-buddy/lib/common_utils.sh\nsource bash-buddy/lib/java_build_utils.sh\n\nreadonly nl=$'\\n' # new line\n\n# shellcheck disable=SC2154\n[ $# -ne 1 ] && cu::die \"need only 1 argument for version!$nl${nl}usage:$nl  $0 4.x.y\"\nreadonly bump_version=\"$1\"\n\n(\n    cu::head_line_echo \"bump cola version of cola-components to $bump_version\"\n    cd ../cola-components/\n\n    jvb::mvn_cmd \\\n        org.codehaus.mojo:versions-maven-plugin:2.16.2:set \\\n        -DgenerateBackupPoms=false \\\n        -DprocessAllModules=true \\\n        -DnewVersion=\"$bump_version\"\n)\n\n(\n    cu::head_line_echo \"bump cola version of cola-archetypes to $bump_version\"\n    cd ../cola-archetypes/\n\n    jvb::mvn_cmd \\\n        org.codehaus.mojo:versions-maven-plugin:2.16.2:set \\\n        -DgenerateBackupPoms=false \\\n        -DnewVersion=\"$bump_version\"\n\n    cu::log_then_run -s \\\n        sed -ri 's~(<cola.components.version>)(.*)(</cola.components.version>)~\\1'\"$bump_version\"'\\3~' \\\n        cola-archetype-service/src/main/resources/archetype-resources/pom.xml \\\n        cola-archetype-web/src/main/resources/archetype-resources/pom.xml\n)\n\n(\n    cu::head_line_echo \"bump cola version of samples to $bump_version\"\n    cd ../cola-samples/\n\n    cu::log_then_run -s \\\n        sed -ri 's~(<cola.components.version>)(.*)(</cola.components.version>)~\\1'\"$bump_version\"'\\3~' \\\n        craftsman/pom.xml\n)\n"
  },
  {
    "path": "scripts/integration_test",
    "content": "#!/bin/bash\nset -eEuo pipefail\ncd \"$(dirname \"$(readlink -f \"$0\")\")\"\n\nBASH_BUDDY_ROOT=\"$(readlink -f bash-buddy)\"\nreadonly BASH_BUDDY_ROOT\nsource \"$BASH_BUDDY_ROOT/lib/trap_error_info.sh\"\nsource \"$BASH_BUDDY_ROOT/lib/common_utils.sh\"\nsource \"$BASH_BUDDY_ROOT/lib/java_build_utils.sh\"\n\n################################################################################\n# ci build logic\n################################################################################\n\nreadonly default_build_jdk_version=17\n# shellcheck disable=SC2034\nreadonly JDK_VERSIONS=(\n  \"$default_build_jdk_version\"\n)\nreadonly default_jh_var_name=\"JAVA${default_build_jdk_version}_HOME\"\n\n# Here use `-D performRelease` intendedly to check release operations.\n#\n# De-activate a maven profile from command line\n#   https://stackoverflow.com/questions/25201430\n#\n# shellcheck disable=SC2034\nJVB_MVN_OPTS=(\n  \"${JVB_DEFAULT_MVN_OPTS[@]}\"\n  -DperformRelease -P'!gen-sign'\n  ${CI_MORE_MVN_OPTS:+${CI_MORE_MVN_OPTS}}\n)\n\n################################################################################\n# ci build logic\n################################################################################\n\ncd ..\n\nextractFirstElementValueFromPom() {\n  (($# == 2)) || die \"${FUNCNAME[0]} need only 2 arguments, actual arguments: $*\"\n\n  local element=$1\n  local pom_file=$2\n  grep \\<\"$element\"'>.*</'\"$element\"\\> \"$pom_file\" | awk -F'</?'\"$element\"\\> 'NR==1 {print $2}'\n}\n\ntest_cola_archetype() {\n  (\n    JVB_MVN_OPTS=(\"${JVB_DEFAULT_MVN_OPTS[@]}\")\n    readonly archetype_name=cola-framework-archetype-service\n\n    cu::head_line_echo \"test archetype:generate by $archetype_name\"\n\n    # NOTE: DO NOT declare archetypeVersion var as readonly, its value is supplied by subshell.\n    archetypeVersion=$(extractFirstElementValueFromPom version cola-archetypes/cola-archetype-service/pom.xml)\n\n    # shellcheck disable=SC2030\n    readonly demo_dir=\"cola-archetypes/target/$archetype_name-demo\"\n    rm -rf \"$demo_dir\"\n    mkdir -p \"$demo_dir\"\n    cd \"$demo_dir\"\n\n    # shellcheck disable=SC2030\n    readonly artifactId=demo-service\n\n    jvb::mvn_cmd archetype:generate \\\n      -DgroupId=com.alibaba.cola.demo.archetype-service \\\n      -DartifactId=\"$artifactId\" \\\n      -Dversion=1.0.0-SNAPSHOT \\\n      -Dpackage=com.alibaba.cola.demo.service \\\n      -DarchetypeGroupId=com.alibaba.cola \\\n      -DarchetypeArtifactId=$archetype_name \\\n      -DarchetypeVersion=\"$archetypeVersion\" \\\n      -DinteractiveMode=false \\\n      -DarchetypeCatalog=local\n\n    cd \"$artifactId\"\n    jvb::mvn_cmd install\n  )\n\n  (\n    JVB_MVN_OPTS=(\"${JVB_DEFAULT_MVN_OPTS[@]}\")\n    readonly archetype_name=cola-framework-archetype-web\n\n    cu::head_line_echo \"test archetype:generate by $archetype_name\"\n\n    # NOTE: DO NOT declare archetypeVersion var as readonly, its value is supplied by subshell.\n    archetypeVersion=$(extractFirstElementValueFromPom version cola-archetypes/cola-archetype-web/pom.xml)\n\n    # shellcheck disable=SC2031\n    readonly demo_dir=\"cola-archetypes/target/$archetype_name-demo\"\n    rm -rf \"$demo_dir\"\n    mkdir -p \"$demo_dir\"\n    cd \"$demo_dir\"\n\n    # shellcheck disable=SC2031\n    readonly artifactId=demo-web\n\n    jvb::mvn_cmd archetype:generate \\\n      -DgroupId=com.alibaba.cola.demo.archetype-web \\\n      -DartifactId=\"$artifactId\" \\\n      -Dversion=1.0.0-SNAPSHOT \\\n      -Dpackage=com.alibaba.cola.demo.web \\\n      -DarchetypeGroupId=com.alibaba.cola \\\n      -DarchetypeArtifactId=$archetype_name \\\n      -DarchetypeVersion=\"$archetypeVersion\" \\\n      -DinteractiveMode=false \\\n      -DarchetypeCatalog=local\n\n    cd \"$artifactId\"\n    jvb::mvn_cmd install\n  )\n}\n\n########################################\n# default jdk 11, do build and test\n########################################\n\n[ -d \"${!default_jh_var_name:-}\" ] || cu::die \"\\$${default_jh_var_name}(${!default_jh_var_name:-}) dir is not existed!\"\nexport JAVA_HOME=\"${!default_jh_var_name}\"\n\ncu::head_line_echo \"build and test with Java $default_build_jdk_version: $JAVA_HOME\"\n\njvb::mvn_cmd clean install\n\ntest_cola_archetype\n\n########################################\n# test by multiply version jdks\n########################################\n\nfor jdk_version in \"${JDK_VERSIONS[@]}\"; do\n  # skip default jdk, already tested above\n  [ \"$jdk_version\" = \"$default_build_jdk_version\" ] && continue\n\n  jh_var_name=\"JAVA${jdk_version}_HOME\"\n  [ -d \"${!jh_var_name:-}\" ] || cu::die \"\\$${jh_var_name}(${!jh_var_name:-}) dir is not existed!\"\n  export JAVA_HOME=\"${!jh_var_name}\"\n\n  cu::head_line_echo \"test with Java $jdk_version: $JAVA_HOME\"\n\n  # just test without build\n  jvb::mvn_cmd surefire:test\n\n  test_cola_archetype\ndone\n"
  },
  {
    "path": "scripts/maven-deploy.md",
    "content": "# COLA发布操作说明\n\nCOLA发布到`Maven`中央库操作过程/CheckList。\n\n## 0. 前置准备与配置\n\n在Maven的`setting.xml`中配置`oss.sonatype.org`账号：\n\n```xml\n\n<servers>\n    <server>\n        <id>ossrh</id>\n        <username>__YOUR_USERNAME__</username>\n        <password>__YOUR_PASSWORD__</password>\n    </server>\n</servers>\n```\n\n更多发布操作说明（如用于`GPG`签名的`GPG`安装与配置），参见：\n\n- OSSRH Guide  \n  https://central.sonatype.org/pages/ossrh-guide.html\n- Deploying to OSSRH with Apache Maven - Introduction  \n  https://central.sonatype.org/pages/apache-maven.html\n\n发布过程与发布文件的查看地址：\n\n- sonatype的发布控制台  \n  https://oss.sonatype.org/index.html\n- Maven中央库的文件查看  \n  https://repo1.maven.org/maven2/com/alibaba/cola/\n\n发布使用`JDK 11`，为了生成`Javadoc`更现代。  \nTODO：这个约束应该要去掉。使用`JDK 8`能发布挺好 :\")\n\n## 1. 发布 COLA Components\n\n先确认版本号，去掉`SNAPSHOT`，如`4.x.y`。  \n更新版本操作可以通过脚本[`bump_cola_version`](bump_cola_version)来统一完成。\n\n在[COLA Components的根目录](../cola-components)，执行发布\n\n```bash\n./mvnw clean && ./mvnw deploy -DperformRelease\n```\n\n## 2. 发布 COLA Archetype\n\n先确认版本号，去掉`SNAPSHOT`，如`4.x.y`：\n\n- 更新 Archetype工程的POM文件的工程版本号：\n    - [`cola-archetypes/pom.xml`](../cola-archetypes/pom.xml)\n    - [`cola-archetype-service/pom.xml`](../cola-archetypes/cola-archetype-service/pom.xml)\n    - [`cola-archetype-web/pom.xml`](../cola-archetypes/cola-archetype-web/pom.xml)\n- 更新 Archetype模板中的POM文件的`cola.components.version`：\n    - [`cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/pom.xml`](../cola-archetypes/cola-archetype-service/src/main/resources/archetype-resources/pom.xml)\n    - [`cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/pom.xml`](../cola-archetypes/cola-archetype-web/src/main/resources/archetype-resources/pom.xml)\n\n更新版本操作可以通过脚本[`bump_cola_version`](bump_cola_version)来统一完成。\n\n在[COLA Archetype的根目录](../cola-archetypes)，执行发布\n\n```bash\n./mvnw clean && ./mvnw deploy -DperformRelease\n```\n\n## 3. 使用发布版本的COLA Archetype重新生成Sample\n\n在[Samples目录](../samples)执行：\n\n```bash\nrm -rf craftsman\n\n./mvnw archetype:generate \\\n    -DgroupId=com.alibaba.craftsman \\\n    -DartifactId=craftsman \\\n    -Dversion=1.0.0-SNAPSHOT \\\n    -Dpackage=com.alibaba.craftsman \\\n    -DarchetypeGroupId=com.alibaba.cola \\\n    -DarchetypeArtifactId=cola-framework-archetype-web \\\n    -DarchetypeVersion=4.x.y \\\n    -DinteractiveMode=false\n```\n\n然后`git`提交Sample。\n"
  }
]