开张大吉。
This commit is contained in:
4
LICENSE
4
LICENSE
@@ -1,4 +1,4 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
@@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
726
README.md
726
README.md
@@ -1 +1,725 @@
|
||||
# fizz-gateway-community
|
||||
|
||||
### 一、部署说明
|
||||
|
||||
#### 安装依赖的软件
|
||||
|
||||
安装以下依赖软件:
|
||||
|
||||
- Redis 2.8或以上版本
|
||||
- MySQL 5.7或以上版本
|
||||
- Apollo配置中心 (可选)
|
||||
- Eureka服务注册中心
|
||||
|
||||
#### 安装Fizz
|
||||
|
||||
1. 管理后台
|
||||
|
||||
从release目录下载 fizz-manager-professional 和 fizz-admin-professional 的安装包,根据包里的readme进行安装。
|
||||
|
||||
2. fizz-gateway-community社区版
|
||||
|
||||
说明:如果使用apollo配置中心,可把application.yml文件内容迁到配置中心(apollo上应用名为:fizz-gateway);使用不使用apollo可去掉下面启动命令里的apollo参数。
|
||||
|
||||
脚本启动:
|
||||
|
||||
1. 下载fizz-gateway-community的最新代码,使用maven构建好并把构建好的fizz-gateway-community-1.0.0.jar和boot.sh放同一目录
|
||||
2. 修改boot.sh脚本的apollo连接,JVM内存配置
|
||||
3. 执行 `./boot.sh start` 命令启动服务,支持 start/stop/restart/status命令
|
||||
|
||||
IDE启动:
|
||||
|
||||
1. 本地clone仓库上的最新代码
|
||||
2. 将项目fizz-gateway导入IDE
|
||||
3. 导入完成后设置项目启动配置,在VM选项中加入`-Denv=dev -Dapollo.meta=http://localhost:66`(Apollo配置中心地址)
|
||||
|
||||
jar启动:
|
||||
|
||||
1. 本地clone仓库上的最新代码
|
||||
2. 在项目根目录fizz-gateway-community下执行Maven命令`mvn clean package -DskipTests=true`打包
|
||||
3. 进入target目录,使用命令`java -jar -Denv=DEV -Dapollo.meta=http://localhost:66 fizz-gateway-community-1.0.0.jar`启动服务
|
||||
|
||||
|
||||
#### 二、授权说明
|
||||
|
||||
1. 网关核心项目fizz-gateway-community社区版本以GNU v3的方式进行的开放,可以免费免费使用。
|
||||
|
||||
2. 管理后台项目fizz-manager-professional,fizz-admin-professional作为商业版本仅开放二进制包的免费下载,而商业项目请联系我们(ewing.lin@wehotelglobal.com)进行授权。
|
||||
|
||||
|
||||
fizz-gateway-community社区版本以GNU v3 license开放:
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
||||
|
||||
230
js/common.js
Normal file
230
js/common.js
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* context 上下文便捷操作函数
|
||||
*
|
||||
*/
|
||||
var common = {
|
||||
/* *********** private function begin *********** */
|
||||
|
||||
// 获取上下文中客户端请求对象
|
||||
getInputReq: function (ctx){
|
||||
if(!ctx || !ctx['input'] || !ctx['input']['request']){
|
||||
return {};
|
||||
}
|
||||
return ctx['input']['request']
|
||||
},
|
||||
|
||||
// 获取上下文步骤中请求接口的请求对象
|
||||
getStepReq: function (ctx, stepName, requestName){
|
||||
if(!ctx || !stepName || !requestName){
|
||||
return {};
|
||||
}
|
||||
if(!ctx[stepName] || !ctx[stepName]['requests'] || !ctx[stepName]['requests'][requestName] ||
|
||||
!ctx[stepName]['requests'][requestName]['request']){
|
||||
return {};
|
||||
}
|
||||
return ctx[stepName]['requests'][requestName]['request'];
|
||||
},
|
||||
|
||||
// 获取上下文步骤中请求接口的响应对象
|
||||
getStepResp: function (ctx, stepName, requestName){
|
||||
if(!ctx || !stepName || !requestName){
|
||||
return {};
|
||||
}
|
||||
if(!ctx[stepName] || !ctx[stepName]['requests'] || !ctx[stepName]['requests'][requestName] ||
|
||||
!ctx[stepName]['requests'][requestName]['response']){
|
||||
return {};
|
||||
}
|
||||
return ctx[stepName]['requests'][requestName]['response'];
|
||||
},
|
||||
|
||||
/* *********** private function end *********** */
|
||||
|
||||
/* *********** input begin ************ */
|
||||
|
||||
/**
|
||||
* 获取客户端请求头
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} headerName 请求头字段名 【选填】,不传时返回所有请求头
|
||||
*/
|
||||
getInputReqHeader: function (ctx, headerName){
|
||||
var req = this.getInputReq(ctx);
|
||||
var headers = req['headers'] || {};
|
||||
return headerName ? headers[headerName] : headers;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取客户端URL请求参数(query string)
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} paramName URL参数名 【选填】,不传时返回所有请求参数
|
||||
*/
|
||||
getInputReqParam: function (ctx, paramName){
|
||||
var req = this.getInputReq(ctx);
|
||||
var params = req['params'] || {};
|
||||
return paramName ? params[paramName] : params;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取客户端请求体
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} field 字段名 【选填】,不传时返回整个请求体
|
||||
*/
|
||||
getInputReqBody: function (ctx, field){
|
||||
var req = this.getInputReq(ctx);
|
||||
var body = req['body'] || {};
|
||||
return field ? body[field] : body;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取返回给客户端的响应头
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} headerName 响应头字段名 【选填】,不传时返回所有响应头
|
||||
*/
|
||||
getInputRespHeader: function (ctx, headerName){
|
||||
var req = this.getInputReq(ctx);
|
||||
var headers = req['headers'] || {};
|
||||
return headerName ? headers[headerName] : headers;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取返回给客户端的响应体
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} field 字段名 【选填】,不传时返回整个响应体
|
||||
*/
|
||||
getInputRespBody: function (ctx, field){
|
||||
var req = this.getInputReq(ctx);
|
||||
var body = req['body'] || {};
|
||||
return field ? body[field] : body;
|
||||
},
|
||||
|
||||
/* *********** input begin ************ */
|
||||
|
||||
/* *********** step request begin ************ */
|
||||
|
||||
/**
|
||||
* 获取步骤中调用的接口的请求头
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} stepName 步骤名【必填】
|
||||
* @param {*} requestName 请求的接口名 【必填】
|
||||
* @param {*} headerName 请求头字段名 【选填】,不传时返回所有请求头
|
||||
*/
|
||||
getStepReqHeader: function (ctx, stepName, requestName, headerName){
|
||||
var req = this.getStepReq(ctx, stepName, requestName);
|
||||
var headers = req['headers'] || {};
|
||||
return headerName ? headers[headerName] : headers;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取步骤中调用的接口的URL参数
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} stepName 步骤名【必填】
|
||||
* @param {*} requestName 请求的接口名 【必填】
|
||||
* @param {*} paramName URL参数名 【选填】,不传时返回所有URL参数
|
||||
*/
|
||||
getStepReqParam: function (ctx, stepName, requestName, paramName){
|
||||
var req = this.getStepReq(ctx, stepName, requestName);
|
||||
var params = req['params'] || {};
|
||||
return paramName ? params[paramName] : params;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取步骤中调用的接口的请求体
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} stepName 步骤名【必填】
|
||||
* @param {*} requestName 请求的接口名 【必填】
|
||||
* @param {*} field 字段名 【选填】,不传时返回整个请求体
|
||||
*/
|
||||
getStepReqBody: function (ctx, stepName, requestName, field){
|
||||
var req = this.getStepReq(ctx, stepName, requestName);
|
||||
var body = req['body'] || {};
|
||||
return field ? body[field] : body;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取步骤中调用的接口的响应头
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} stepName 步骤名【必填】
|
||||
* @param {*} requestName 请求的接口名 【必填】
|
||||
* @param {*} headerName 响应头字段名 【选填】,不传时返回所有响应头
|
||||
*/
|
||||
getStepRespHeader: function (ctx, stepName, requestName, headerName){
|
||||
var resp = this.getStepResp(ctx, stepName, requestName);
|
||||
var headers = resp['headers'] || {};
|
||||
return headerName ? headers[headerName] : headers;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取步骤中调用的接口的响应头
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} stepName 步骤名【必填】
|
||||
* @param {*} requestName 请求的接口名 【必填】
|
||||
* @param {*} field 字段名 【选填】,不传时返回整个响应头
|
||||
*/
|
||||
getStepRespBody: function (ctx, stepName, requestName, field){
|
||||
var resp = this.getStepResp(ctx, stepName, requestName);
|
||||
var body = resp['body'] || {};
|
||||
return field ? body[field] : body;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取步骤结果
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} stepName 步骤名【必填】
|
||||
* @param {*} field 字段名 【选填】,不传时返回整个步骤结果对象
|
||||
*/
|
||||
getStepResult: function (ctx, stepName, field){
|
||||
if(!ctx || !stepName || !ctx[stepName]){
|
||||
return {};
|
||||
}
|
||||
var result = ctx[stepName]['result'] || {};
|
||||
return field ? result[field] : result;
|
||||
}
|
||||
|
||||
/* *********** step request end ************ */
|
||||
|
||||
,/**
|
||||
** 乘法函数,用来得到精确的乘法结果
|
||||
** 说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。
|
||||
** 调用:accMul(arg1,arg2)
|
||||
** 返回值:arg1乘以 arg2的精确结果
|
||||
**/
|
||||
accMul:function (arg1, arg2) {
|
||||
var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
|
||||
try {
|
||||
m += s1.split(".")[1].length;
|
||||
} catch (e) {
|
||||
}
|
||||
try {
|
||||
m += s2.split(".")[1].length;
|
||||
} catch (e) {
|
||||
}
|
||||
return Number(s1.replace(".", "")) * Number(s2.replace(".", ""))
|
||||
/ Math.pow(10, m);
|
||||
},
|
||||
|
||||
/**
|
||||
** 除法函数,用来得到精确的除法结果
|
||||
** 说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。
|
||||
** 调用:accDiv(arg1,arg2)
|
||||
** 返回值:arg1除以arg2的精确结果
|
||||
**/
|
||||
accDiv:function (arg1, arg2) {
|
||||
var t1 = 0, t2 = 0, r1, r2;
|
||||
try {
|
||||
t1 = arg1.toString().split(".")[1].length;
|
||||
} catch (e) {
|
||||
}
|
||||
try {
|
||||
t2 = arg2.toString().split(".")[1].length;
|
||||
} catch (e) {
|
||||
}
|
||||
with (Math) {
|
||||
r1 = Number(arg1.toString().replace(".", ""));
|
||||
r2 = Number(arg2.toString().replace(".", ""));
|
||||
return (r1 / r2) * pow(10, t2 - t1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
65
js/context.js
Normal file
65
js/context.js
Normal file
@@ -0,0 +1,65 @@
|
||||
// 上下文数据结构设计
|
||||
// 上下文,用于保存客户输入输出和每个步骤的输入与输出结果
|
||||
var context = {
|
||||
// 是否DEBUG模式
|
||||
debug:false,
|
||||
|
||||
// 各个操作的耗时
|
||||
elapsedTimes: [{
|
||||
[actionName]: 123, // 操作名称:耗时
|
||||
}],
|
||||
|
||||
// 客户输入和接口的返回结果
|
||||
input: {
|
||||
request:{
|
||||
path: "",
|
||||
method: "GET/POST",
|
||||
headers: {},
|
||||
body: {},
|
||||
params: {}
|
||||
},
|
||||
response: { // 聚合接口的响应
|
||||
headers: {},
|
||||
body: {}
|
||||
}
|
||||
},
|
||||
|
||||
// 步骤
|
||||
step1: {
|
||||
requests: {
|
||||
// 接口1
|
||||
request1: {
|
||||
// 请求相关参数
|
||||
request:{
|
||||
url: "",
|
||||
method: "GET/POST",
|
||||
headers: {},
|
||||
body: {}
|
||||
},
|
||||
// 根据转换规则转换后的接口响应
|
||||
response: {
|
||||
headers: {},
|
||||
body: {}
|
||||
}
|
||||
},
|
||||
// 接口2
|
||||
request2: {
|
||||
request:{
|
||||
url: "",
|
||||
method: "GET/POST",
|
||||
headers: {},
|
||||
body: {}
|
||||
},
|
||||
response: {
|
||||
headers: {},
|
||||
body: {}
|
||||
}
|
||||
}
|
||||
//...
|
||||
},
|
||||
|
||||
// 步骤结果
|
||||
result: {}
|
||||
|
||||
}
|
||||
}
|
||||
BIN
lib/json-schema-validator-i18n-support-1.0.39_1.jar
Normal file
BIN
lib/json-schema-validator-i18n-support-1.0.39_1.jar
Normal file
Binary file not shown.
310
mvnw
vendored
Executable file
310
mvnw
vendored
Executable file
@@ -0,0 +1,310 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||
else
|
||||
export JAVA_HOME="/Library/Java/Home"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="`which javac`"
|
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=`which readlink`
|
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||
else
|
||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||
fi
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="`which java`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=`cd "$wdir/.."; pwd`
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
##########################################################################################
|
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
##########################################################################################
|
||||
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||
fi
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||
fi
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
else
|
||||
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
fi
|
||||
while IFS="=" read key value; do
|
||||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||
esac
|
||||
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Downloading from: $jarUrl"
|
||||
fi
|
||||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if $cygwin; then
|
||||
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found wget ... using wget"
|
||||
fi
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget "$jarUrl" -O "$wrapperJarPath"
|
||||
else
|
||||
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found curl ... using curl"
|
||||
fi
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl -o "$wrapperJarPath" "$jarUrl" -f
|
||||
else
|
||||
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
|
||||
fi
|
||||
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Falling back to using Java to download"
|
||||
fi
|
||||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaClass=`cygpath --path --windows "$javaClass"`
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
# Compiling the Java class
|
||||
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||
fi
|
||||
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
# Running the downloader
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Running MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo $MAVEN_PROJECTBASEDIR
|
||||
fi
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||
fi
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||
182
mvnw.cmd
vendored
Normal file
182
mvnw.cmd
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
|
||||
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||
)
|
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Found %WRAPPER_JAR%
|
||||
)
|
||||
) else (
|
||||
if not "%MVNW_REPOURL%" == "" (
|
||||
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %DOWNLOAD_URL%
|
||||
)
|
||||
|
||||
powershell -Command "&{"^
|
||||
"$webclient = new-object System.Net.WebClient;"^
|
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||
"}"^
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||
@REM work with both Windows and non-Windows executions.
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||
|
||||
exit /B %ERROR_CODE%
|
||||
198
pom.xml
Normal file
198
pom.xml
Normal file
@@ -0,0 +1,198 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.9.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<groupId>com.wehotel</groupId>
|
||||
<artifactId>fizz-gateway-community</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<name>fizz-gateway</name>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<log4j2.version>2.13.3</log4j2.version>
|
||||
<!--<reactor-bom.version>Dysprosium-SR7</reactor-bom.version>
|
||||
<spring-framework.version>5.2.6.RELEASE</spring-framework.version>
|
||||
<spring-data-releasetrain.version>Moore-SR7</spring-data-releasetrain.version>
|
||||
<lettuce.version>5.3.0.RELEASE</lettuce.version>
|
||||
<netty.version>4.1.50.Final</netty.version>-->
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.networknt</groupId>
|
||||
<artifactId>json-schema-validator-i18n-support</artifactId>
|
||||
<version>1.0.39_1</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${project.basedir}/lib/json-schema-validator-i18n-support-1.0.39_1.jar</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||
</dependency>
|
||||
<!--<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-web</artifactId>
|
||||
<version>2.11.2</version>
|
||||
</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.73</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.skyscreamer</groupId>
|
||||
<artifactId>jsonassert</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.minidev</groupId>
|
||||
<artifactId>json-smart</artifactId>
|
||||
<version>2.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jettison</groupId>
|
||||
<artifactId>jettison</artifactId>
|
||||
<version>1.3.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20190722</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tapestry</groupId>
|
||||
<artifactId>tapestry-json</artifactId>
|
||||
<version>5.4.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.11</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<version>2.4.20</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.14</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.ctrip.framework.apollo</groupId>
|
||||
<artifactId>apollo-client</artifactId>
|
||||
<version>1.5.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
<version>2.2.4.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>snack3</artifactId>
|
||||
<version>3.1.6.4</version>
|
||||
</dependency>
|
||||
|
||||
<!--<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-epoll</artifactId>
|
||||
<classifier>linux-x86_64</classifier>
|
||||
</dependency>-->
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<includeSystemScope>true</includeSystemScope>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
150
sh/boot.sh
Normal file
150
sh/boot.sh
Normal file
@@ -0,0 +1,150 @@
|
||||
#!/bin/bash
|
||||
|
||||
#进入脚本所在目录
|
||||
cd `dirname $0`
|
||||
|
||||
#变量定义
|
||||
APOLLO_META_SERVER=http://localhost:66
|
||||
ENV=dev
|
||||
APP_NAME=fizz-gateway-community-1.0.0.jar
|
||||
APP_DEP_DIR=/data/webapps/fizz-gateway
|
||||
APP_LOG_DIR=/data/logs/fizz-gateway
|
||||
JAVA_CMD=/usr/local/java/bin/java
|
||||
PID_FILE="${APP_LOG_DIR}/tpid"
|
||||
CHECK_COUNT=3
|
||||
SERVER_IP="` ip a | egrep "brd" | grep inet | awk '{print $2}' | sed 's#/24##g'`"
|
||||
|
||||
#创建应用目录
|
||||
mkdir -p ${APP_DEP_DIR}
|
||||
|
||||
#创建日志目录
|
||||
mkdir -p ${APP_LOG_DIR}
|
||||
|
||||
#进入应用所在目录(虽然都是绝对路径,但有些应用需要进入应用目录才能启动成功)
|
||||
cd ${APP_DEP_DIR}
|
||||
|
||||
JAVA_OPTS="-Xms256m -Xmx4096m \
|
||||
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m \
|
||||
-XX:+AggressiveOpts \
|
||||
-XX:+UseBiasedLocking \
|
||||
-XX:+UseG1GC \
|
||||
-XX:+HeapDumpOnOutOfMemoryError \
|
||||
-XX:-OmitStackTraceInFastThrow \
|
||||
-verbose:gc \
|
||||
-XX:+PrintGCDetails \
|
||||
-XX:+PrintGCDateStamps \
|
||||
-XX:+PrintHeapAtGC \
|
||||
-Xloggc:${APP_LOG_DIR}/${START_DATE_TIME}.gc \
|
||||
-XX:+HeapDumpOnOutOfMemoryError \
|
||||
-XX:HeapDumpPath=${APP_LOG_DIR}/dump.logs \
|
||||
-Dorg.jboss.netty.epollBugWorkaround=true \
|
||||
-Dio.netty.leakDetectionLevel=PARANOID -Dio.netty.leakDetection.targetRecords=60 \
|
||||
-Dio.netty.allocator.type=unpooled \
|
||||
-Dio.netty.noPreferDirect=true \
|
||||
-Dio.netty.noUnsafe=true "
|
||||
|
||||
#进程状态标识变量,1为存在,0为不存在
|
||||
PID_FLAG=0
|
||||
|
||||
#检查服务进程是否存在
|
||||
checktpid() {
|
||||
TPID=`cat ${PID_FILE} | awk '{print $1}'`
|
||||
TPID=`ps -aef | grep ${TPID} | awk '{print $2}' | grep ${TPID}`
|
||||
if [[ ${TPID} ]]
|
||||
then
|
||||
PID_FLAG=1
|
||||
else
|
||||
PID_FLAG=0
|
||||
fi
|
||||
}
|
||||
|
||||
#启动服务函数
|
||||
start() {
|
||||
#检查进程状态
|
||||
checktpid
|
||||
if [[ ${PID_FLAG} -ne 0 ]]
|
||||
then
|
||||
echo "warn: $APP_NAME already started, ignoring startup request."
|
||||
else
|
||||
echo "starting $APP_NAME ..."
|
||||
rm -f ${PID_FILE}
|
||||
#rm -rf ${APP_LOG_DIR}/flumeES/*
|
||||
${JAVA_CMD} -jar ${JAVA_OPTS} -Denv=$ENV -Dapollo.meta=${APOLLO_META_SERVER} ${APP_DEP_DIR}/${APP_NAME} > ${APP_LOG_DIR}/${APP_NAME}.log 2>&1 &
|
||||
echo $! > ${PID_FILE}
|
||||
fi
|
||||
}
|
||||
|
||||
#关闭服务函数
|
||||
stop() {
|
||||
#检查进程状态
|
||||
checktpid
|
||||
|
||||
if [[ ${PID_FLAG} -ne 0 ]]
|
||||
then
|
||||
echo "stoping $APP_NAME..."
|
||||
|
||||
#循环检查进程3次,每次睡眠2秒
|
||||
for((i=1;i<=${CHECK_COUNT};i++))
|
||||
do
|
||||
kill -9 ${TPID}
|
||||
sleep 2
|
||||
|
||||
#检查进程状态
|
||||
checktpid
|
||||
|
||||
if [[ ${PID_FLAG} -eq 0 ]]
|
||||
then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
#如果以上正常关闭进程都失败,则强制关闭
|
||||
if [[ ${PID_FLAG} -ne 0 ]]
|
||||
then
|
||||
echo "stoping use kill -9..."
|
||||
kill -9 ${TPID}
|
||||
sleep 2
|
||||
else
|
||||
echo "$APP_NAME Stopped!"
|
||||
fi
|
||||
|
||||
else
|
||||
echo "warn:$APP_NAME is not runing"
|
||||
fi
|
||||
}
|
||||
|
||||
#检测进程状态函数
|
||||
status() {
|
||||
#检查进程状态
|
||||
checktpid
|
||||
|
||||
if [[ ${PID_FLAG} -eq 0 ]]
|
||||
then
|
||||
echo "$APP_NAME is not runing"
|
||||
else
|
||||
echo "$APP_NAME is runing"
|
||||
fi
|
||||
}
|
||||
|
||||
#####脚本执行入口#####
|
||||
case "$1" in
|
||||
'start')
|
||||
start
|
||||
;;
|
||||
'stop')
|
||||
stop
|
||||
;;
|
||||
'restart')
|
||||
stop
|
||||
start
|
||||
;;
|
||||
'status')
|
||||
status
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
43
src/main/java/com/wehotel/FizzGatewayApplication.java
Normal file
43
src/main/java/com/wehotel/FizzGatewayApplication.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel;
|
||||
|
||||
import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
* @author francis
|
||||
*/
|
||||
@SpringBootApplication(exclude = {ErrorWebFluxAutoConfiguration.class, RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class})
|
||||
// @EnableApolloConfig
|
||||
@EnableDiscoveryClient
|
||||
public class FizzGatewayApplication {
|
||||
|
||||
public static ConfigurableApplicationContext appContext;
|
||||
|
||||
public static void main(String[] args) {
|
||||
FizzGatewayApplication.appContext = SpringApplication.run(FizzGatewayApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.apollo;
|
||||
|
||||
import com.ctrip.framework.apollo.Config;
|
||||
import com.ctrip.framework.apollo.ConfigChangeListener;
|
||||
import com.ctrip.framework.apollo.ConfigService;
|
||||
import com.ctrip.framework.apollo.model.ConfigChange;
|
||||
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.core.LoggerContext;
|
||||
import org.apache.logging.log4j.core.config.Configuration;
|
||||
import org.apache.logging.log4j.core.config.ConfigurationFactory;
|
||||
import org.apache.logging.log4j.core.config.ConfigurationSource;
|
||||
import org.apache.logging.log4j.core.config.Order;
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
import org.apache.logging.log4j.core.config.properties.PropertiesConfigurationBuilder;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* log4j2初始化时通过该类从Apollo加载相应的log4j2的配置信息
|
||||
* 当通过Apollo修改log4j2配置后,能立即生效。例如,动态修改日志级别。
|
||||
* @author honam
|
||||
* @date 2019-08-05
|
||||
*/
|
||||
// @Plugin(name = "ApolloLog4j2ConfigurationFactory", category = ConfigurationFactory.CATEGORY)
|
||||
// @Order(50)
|
||||
public class ApolloLog4j2ConfigurationFactory extends ConfigurationFactory {
|
||||
|
||||
private static final String LOG4J2_NAMESPACE = "log4j2";
|
||||
|
||||
@Override
|
||||
protected String[] getSupportedTypes() {
|
||||
return new String[]{"*"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
|
||||
return getConfiguration(loggerContext, source.toString(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration getConfiguration(LoggerContext loggerContext, String name, URI configLocation) {
|
||||
// 从Apollo读取log4j2配置
|
||||
Config config = ConfigService.getConfig(LOG4J2_NAMESPACE);
|
||||
Set<String> propertyNames = config.getPropertyNames();
|
||||
|
||||
Properties properties = new Properties();
|
||||
for (String propertyName : propertyNames) {
|
||||
String propertyValue = config.getProperty(propertyName, null);
|
||||
properties.setProperty(propertyName, propertyValue);
|
||||
}
|
||||
|
||||
// 添加log4j2配置的监听器
|
||||
config.addChangeListener(new Log4j2ConfigChangeListener(properties));
|
||||
|
||||
// 构造log4j2的Configuration
|
||||
return new PropertiesConfigurationBuilder()
|
||||
.setRootProperties(copyProperties(properties))
|
||||
.setLoggerContext(loggerContext)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制Properties
|
||||
*
|
||||
* @param properties 原Properties对象
|
||||
* @return 新Properties对象
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Properties copyProperties(Properties properties) {
|
||||
Properties newProperties = new Properties();
|
||||
|
||||
Enumeration<String> enumeration = (Enumeration<String>) properties.propertyNames();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
String propertyName = enumeration.nextElement();
|
||||
newProperties.put(propertyName, properties.getProperty(propertyName));
|
||||
}
|
||||
return newProperties;
|
||||
}
|
||||
|
||||
private class Log4j2ConfigChangeListener implements ConfigChangeListener {
|
||||
|
||||
private Properties configProperties;
|
||||
|
||||
Log4j2ConfigChangeListener(Properties configProperties) {
|
||||
this.configProperties = configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(ConfigChangeEvent changeEvent) {
|
||||
String newValue;
|
||||
ConfigChange configChange;
|
||||
|
||||
for (String changedKey : changeEvent.changedKeys()) {
|
||||
configChange = changeEvent.getChange(changedKey);
|
||||
newValue = configChange.getNewValue();
|
||||
if (newValue != null) {
|
||||
configProperties.put(changedKey, newValue);
|
||||
} else {
|
||||
configProperties.remove(changedKey);
|
||||
}
|
||||
}
|
||||
|
||||
// 构造新配置并应用到LoggerContext
|
||||
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
|
||||
Configuration newConfiguration = new PropertiesConfigurationBuilder()
|
||||
.setRootProperties(copyProperties(configProperties))
|
||||
.setLoggerContext(ctx)
|
||||
.build();
|
||||
ctx.setConfiguration(newConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
40
src/main/java/com/wehotel/config/AggrWebClientConfig.java
Normal file
40
src/main/java/com/wehotel/config/AggrWebClientConfig.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = AggrWebClientConfig.prefix)
|
||||
public class AggrWebClientConfig extends WebClientConfig {
|
||||
|
||||
protected static final String prefix = "aggr-webclient";
|
||||
|
||||
public static final String aggrWebClient = "aggrWebClient";
|
||||
|
||||
@Bean(aggrWebClient)
|
||||
public WebClient webClient() {
|
||||
return super.webClient();
|
||||
}
|
||||
}
|
||||
40
src/main/java/com/wehotel/config/AppConfigProperties.java
Normal file
40
src/main/java/com/wehotel/config/AppConfigProperties.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author unknown
|
||||
*/
|
||||
@Configuration
|
||||
public class AppConfigProperties {
|
||||
|
||||
@Value("${spring.profiles.active}")
|
||||
private String env;
|
||||
|
||||
public String getEnv() {
|
||||
return env;
|
||||
}
|
||||
public void setEnv(String env) {
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
40
src/main/java/com/wehotel/config/ProxyWebClientConfig.java
Normal file
40
src/main/java/com/wehotel/config/ProxyWebClientConfig.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = ProxyWebClientConfig.prefix)
|
||||
public class ProxyWebClientConfig extends WebClientConfig {
|
||||
|
||||
protected static final String prefix = "proxy-webclient";
|
||||
|
||||
public static final String proxyWebClient = "proxyWebClient";
|
||||
|
||||
@Bean(proxyWebClient)
|
||||
public WebClient webClient() {
|
||||
return super.webClient();
|
||||
}
|
||||
}
|
||||
75
src/main/java/com/wehotel/config/RedisReactiveConfig.java
Normal file
75
src/main/java/com/wehotel/config/RedisReactiveConfig.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.config;
|
||||
|
||||
import io.lettuce.core.ClientOptions;
|
||||
import io.lettuce.core.resource.ClientResources;
|
||||
import io.lettuce.core.resource.DefaultClientResources;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
|
||||
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
public abstract class RedisReactiveConfig {
|
||||
|
||||
protected static final Logger log = LoggerFactory.getLogger(RedisReactiveConfig.class);
|
||||
|
||||
// this should not be changed unless there is a truly good reason to do so
|
||||
private static final int ps = Runtime.getRuntime().availableProcessors();
|
||||
private static final ClientResources clientResources = DefaultClientResources.builder()
|
||||
.ioThreadPoolSize(ps)
|
||||
.computationThreadPoolSize(ps)
|
||||
.build();
|
||||
|
||||
private RedisReactiveProperties redisReactiveProperties;
|
||||
|
||||
public RedisReactiveConfig(RedisReactiveProperties properties) {
|
||||
redisReactiveProperties = properties;
|
||||
}
|
||||
|
||||
public ReactiveStringRedisTemplate reactiveStringRedisTemplate(ReactiveRedisConnectionFactory fact) {
|
||||
return new ReactiveStringRedisTemplate(fact);
|
||||
}
|
||||
|
||||
public ReactiveRedisConnectionFactory lettuceConnectionFactory() {
|
||||
|
||||
log.info("connect to " + redisReactiveProperties);
|
||||
|
||||
RedisStandaloneConfiguration rcs = new RedisStandaloneConfiguration(redisReactiveProperties.getHost(), redisReactiveProperties.getPort());
|
||||
String password = redisReactiveProperties.getPassword();
|
||||
if (password != null) {
|
||||
rcs.setPassword(password);
|
||||
}
|
||||
rcs.setDatabase(redisReactiveProperties.getDatabase());
|
||||
|
||||
LettucePoolingClientConfiguration ccs = LettucePoolingClientConfiguration.builder()
|
||||
.clientResources(clientResources)
|
||||
.clientOptions(ClientOptions.builder().publishOnScheduler(true).build())
|
||||
.poolConfig(new GenericObjectPoolConfig())
|
||||
.build();
|
||||
|
||||
return new LettuceConnectionFactory(rcs, ccs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.config;
|
||||
|
||||
import com.wehotel.util.Constants;
|
||||
import com.wehotel.util.Utils;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
public abstract class RedisReactiveProperties {
|
||||
|
||||
private String host = "127.0.0.1";
|
||||
private int port = 6379;
|
||||
private String password;
|
||||
private int database = 0;
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public int getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
public void setDatabase(int database) {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder b = new StringBuilder(48);
|
||||
appendTo(b);
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
public void appendTo(StringBuilder b) {
|
||||
b.append(Constants.Symbol.LEFT_BRACE);
|
||||
Utils.addTo(b, "host", Constants.Symbol.EQUAL, host, Constants.Symbol.SPACE_STR);
|
||||
Utils.addTo(b, "port", Constants.Symbol.EQUAL, port, Constants.Symbol.SPACE_STR);
|
||||
Utils.addTo(b, "password", Constants.Symbol.EQUAL, password, Constants.Symbol.SPACE_STR);
|
||||
Utils.addTo(b, "database", Constants.Symbol.EQUAL, database, Constants.Symbol.EMPTY);
|
||||
b.append(Constants.Symbol.RIGHT_BRACE);
|
||||
}
|
||||
}
|
||||
152
src/main/java/com/wehotel/config/SystemConfig.java
Normal file
152
src/main/java/com/wehotel/config/SystemConfig.java
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.config;
|
||||
|
||||
import com.ctrip.framework.apollo.model.ConfigChange;
|
||||
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
|
||||
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
|
||||
import com.wehotel.plugin.auth.GatewayGroup;
|
||||
import com.wehotel.util.Constants;
|
||||
import com.wehotel.util.JacksonUtils;
|
||||
import com.wehotel.util.NetworkUtils;
|
||||
import com.wehotel.util.WebUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.management.RuntimeErrorException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
@Configuration
|
||||
public class SystemConfig {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SystemConfig.class);
|
||||
|
||||
@Value("${log.response-body:false}")
|
||||
private boolean logResponseBody;
|
||||
|
||||
@Value("${log.headers:x}")
|
||||
private String logHeaders;
|
||||
|
||||
private Set<String> logHeaderSet = new HashSet<>();
|
||||
|
||||
@Value("${gateway-group:}")
|
||||
private String gatewayGroup;
|
||||
|
||||
private Map<String, Set<Character>> server2gatewayGroupSetMap = new HashMap<>();
|
||||
|
||||
private Set<Character> currentServerGatewayGroupSet;
|
||||
|
||||
@Value("${spring.profiles.active}")
|
||||
private String profile;
|
||||
|
||||
public Set<Character> getCurrentServerGatewayGroupSet() {
|
||||
return currentServerGatewayGroupSet;
|
||||
}
|
||||
|
||||
public Set<String> getLogHeaderSet() {
|
||||
return logHeaderSet;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void afterPropertiesSet() {
|
||||
afterLogResponseBodySet();
|
||||
afterLogHeadersSet();
|
||||
afterGatewayGroupSet();
|
||||
}
|
||||
|
||||
private void afterLogResponseBodySet() {
|
||||
WebUtils.logResponseBody = logResponseBody;
|
||||
log.info("log response body: " + logResponseBody);
|
||||
}
|
||||
|
||||
private void afterLogHeadersSet() {
|
||||
logHeaderSet.clear();
|
||||
Arrays.stream(StringUtils.split(logHeaders, Constants.Symbol.COMMA)).forEach(h -> {
|
||||
logHeaderSet.add(h);
|
||||
});
|
||||
WebUtils.logHeaderSet = logHeaderSet;
|
||||
log.info("log header list: " + logHeaderSet.toString());
|
||||
}
|
||||
|
||||
private void afterGatewayGroupSet() {
|
||||
server2gatewayGroupSetMap.clear();
|
||||
if (StringUtils.isNotBlank(gatewayGroup)) {
|
||||
Arrays.stream(StringUtils.split(gatewayGroup, ';')).forEach(
|
||||
gg -> {
|
||||
Character group = Character.valueOf(gg.charAt(0));
|
||||
String servers = gg.substring(gg.indexOf(':') + 1);
|
||||
Arrays.stream(StringUtils.split(servers, ',')).forEach(
|
||||
s -> {
|
||||
Set<Character> gs = server2gatewayGroupSetMap.get(s);
|
||||
if (gs == null) {
|
||||
gs = new HashSet<>();
|
||||
server2gatewayGroupSetMap.put(s, gs);
|
||||
}
|
||||
gs.add(group);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
log.info("server 2 gateway group set map: " + JacksonUtils.writeValueAsString(server2gatewayGroupSetMap));
|
||||
String serverIp = NetworkUtils.getServerIp();
|
||||
currentServerGatewayGroupSet = server2gatewayGroupSetMap.get(serverIp);
|
||||
if (currentServerGatewayGroupSet == null) {
|
||||
if (Constants.Profiles.DEV.equals(profile) || Constants.Profiles.TEST.equals(profile)) {
|
||||
currentServerGatewayGroupSet = new HashSet<>();
|
||||
currentServerGatewayGroupSet.add(GatewayGroup.C);
|
||||
currentServerGatewayGroupSet.add(GatewayGroup.B);
|
||||
currentServerGatewayGroupSet.add(GatewayGroup.T);
|
||||
server2gatewayGroupSetMap.put(serverIp, currentServerGatewayGroupSet);
|
||||
} else {
|
||||
throw new RuntimeException("no gateway group config for " + serverIp);
|
||||
}
|
||||
}
|
||||
log.info("current server: " + serverIp + ", belong to: " + currentServerGatewayGroupSet);
|
||||
}
|
||||
|
||||
@ApolloConfigChangeListener
|
||||
private void configChangeListter(ConfigChangeEvent cce) {
|
||||
cce.changedKeys().forEach(
|
||||
k -> {
|
||||
ConfigChange c = cce.getChange(k);
|
||||
String p = c.getPropertyName();
|
||||
String ov = c.getOldValue();
|
||||
String nv = c.getNewValue();
|
||||
log.info(p + " old: " + ov + ", new: " + nv);
|
||||
if (p.equals("log.response-body")) {
|
||||
logResponseBody = Boolean.valueOf(nv);
|
||||
afterLogResponseBodySet();
|
||||
} else if (p.equals("log.headers")) {
|
||||
logHeaders = nv;
|
||||
afterLogHeadersSet();
|
||||
} else if (p.equals("gateway-group")) {
|
||||
gatewayGroup = nv;
|
||||
afterGatewayGroupSet();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
232
src/main/java/com/wehotel/config/WebClientConfig.java
Normal file
232
src/main/java/com/wehotel/config/WebClientConfig.java
Normal file
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.config;
|
||||
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||
import io.netty.channel.AdaptiveRecvByteBufAllocator;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.PreferHeapByteBufAllocator;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||
import io.netty.handler.timeout.WriteTimeoutHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.ReactorResourceFactory;
|
||||
import org.springframework.web.reactive.function.client.ExchangeStrategies;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.netty.resources.ConnectionProvider;
|
||||
import reactor.netty.resources.LoopResources;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
public abstract class WebClientConfig {
|
||||
|
||||
protected static final Logger log = LoggerFactory.getLogger(WebClientConfig.class);
|
||||
|
||||
private String name;
|
||||
|
||||
private int maxConnections = 2_000;
|
||||
|
||||
private Duration maxIdleTime = Duration.ofMillis(40_000);
|
||||
|
||||
private Duration pendingAcquireTimeout = Duration.ofMillis(6_000);
|
||||
|
||||
private long connReadTimeout = 20_000;
|
||||
|
||||
private long connWriteTimeout = 20_000;
|
||||
|
||||
private int chConnTimeout = 20_000;
|
||||
|
||||
private boolean chTcpNodelay = true;
|
||||
|
||||
private boolean chSoKeepAlive = true;
|
||||
|
||||
private boolean compress = false;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = "wc-" + name;
|
||||
}
|
||||
|
||||
public int getMaxConnections() {
|
||||
return maxConnections;
|
||||
}
|
||||
|
||||
public void setMaxConnections(int maxConnections) {
|
||||
this.maxConnections = maxConnections;
|
||||
}
|
||||
|
||||
public Duration getMaxIdleTime() {
|
||||
return maxIdleTime;
|
||||
}
|
||||
|
||||
public void setMaxIdleTime(long maxIdleTime) {
|
||||
this.maxIdleTime = Duration.ofMillis(maxIdleTime);
|
||||
}
|
||||
|
||||
public Duration getPendingAcquireTimeout() {
|
||||
return pendingAcquireTimeout;
|
||||
}
|
||||
|
||||
public void setPendingAcquireTimeout(long pendingAcquireTimeout) {
|
||||
this.pendingAcquireTimeout = Duration.ofMillis(pendingAcquireTimeout);
|
||||
}
|
||||
|
||||
public long getConnReadTimeout() {
|
||||
return connReadTimeout;
|
||||
}
|
||||
|
||||
public void setConnReadTimeout(long connReadTimeout) {
|
||||
this.connReadTimeout = connReadTimeout;
|
||||
}
|
||||
|
||||
public long getConnWriteTimeout() {
|
||||
return connWriteTimeout;
|
||||
}
|
||||
|
||||
public void setConnWriteTimeout(long connWriteTimeout) {
|
||||
this.connWriteTimeout = connWriteTimeout;
|
||||
}
|
||||
|
||||
public int getChConnTimeout() {
|
||||
return chConnTimeout;
|
||||
}
|
||||
|
||||
public void setChConnTimeout(int chConnTimeout) {
|
||||
this.chConnTimeout = chConnTimeout;
|
||||
}
|
||||
|
||||
public boolean isChTcpNodelay() {
|
||||
return chTcpNodelay;
|
||||
}
|
||||
|
||||
public void setChTcpNodelay(boolean chTcpNodelay) {
|
||||
this.chTcpNodelay = chTcpNodelay;
|
||||
}
|
||||
|
||||
public boolean isChSoKeepAlive() {
|
||||
return chSoKeepAlive;
|
||||
}
|
||||
|
||||
public void setChSoKeepAlive(boolean chSoKeepAlive) {
|
||||
this.chSoKeepAlive = chSoKeepAlive;
|
||||
}
|
||||
|
||||
public boolean isCompress() {
|
||||
return compress;
|
||||
}
|
||||
|
||||
public void setCompress(boolean compress) {
|
||||
this.compress = compress;
|
||||
}
|
||||
|
||||
private ConnectionProvider getConnectionProvider() {
|
||||
String cpName = name + "-cp";
|
||||
ConnectionProvider cp = ConnectionProvider.builder(cpName).maxConnections(maxConnections)
|
||||
.pendingAcquireTimeout(pendingAcquireTimeout).maxIdleTime(maxIdleTime).build();
|
||||
log.info(cpName + ' ' + cp);
|
||||
return cp;
|
||||
}
|
||||
|
||||
private LoopResources getLoopResources() {
|
||||
String elPrefix = name + "-el";
|
||||
// LoopResources lr = LoopResources.create(elPrefix, 1, Runtime.getRuntime().availableProcessors(), true);
|
||||
LoopResources lr = LoopResources.create(elPrefix, Runtime.getRuntime().availableProcessors(), true);
|
||||
lr.onServer(false);
|
||||
log.info(name + "-lr " + lr);
|
||||
return lr;
|
||||
}
|
||||
|
||||
protected ReactorResourceFactory reactorResourceFactory() {
|
||||
ReactorResourceFactory fact = new ReactorResourceFactory();
|
||||
fact.setUseGlobalResources(false);
|
||||
fact.setConnectionProvider(getConnectionProvider());
|
||||
fact.setLoopResources(getLoopResources());
|
||||
fact.afterPropertiesSet();
|
||||
return fact;
|
||||
}
|
||||
|
||||
public WebClient webClient() {
|
||||
log.info(this.toString());
|
||||
// return WebClient.builder().exchangeStrategies(ExchangeStrategies.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build())
|
||||
// .clientConnector(new ReactorClientHttpConnector(reactorResourceFactory(), httpClient -> {
|
||||
// return httpClient.compress(compress).tcpConfiguration(tcpClient -> {
|
||||
// return tcpClient.doOnConnected(connection -> {
|
||||
// connection.addHandlerLast(new ReadTimeoutHandler( connReadTimeout, TimeUnit.MILLISECONDS))
|
||||
// .addHandlerLast(new WriteTimeoutHandler( connWriteTimeout, TimeUnit.MILLISECONDS));
|
||||
// }).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, chConnTimeout)
|
||||
// .option(ChannelOption.TCP_NODELAY, chTcpNodelay)
|
||||
// .option(ChannelOption.SO_KEEPALIVE, chSoKeepAlive)
|
||||
// .option(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT);
|
||||
// });
|
||||
// })).build();
|
||||
|
||||
ConnectionProvider cp = getConnectionProvider();
|
||||
LoopResources lr = getLoopResources();
|
||||
HttpClient httpClient = HttpClient.create(cp).compress(compress).tcpConfiguration(
|
||||
tcpClient -> {
|
||||
return tcpClient.runOn(lr, false)
|
||||
// .runOn(lr)
|
||||
// .bootstrap(
|
||||
// bootstrap -> (
|
||||
// bootstrap.channel(NioSocketChannel.class)
|
||||
// )
|
||||
// )
|
||||
.doOnConnected(
|
||||
connection -> {
|
||||
connection.addHandlerLast(new ReadTimeoutHandler( connReadTimeout, TimeUnit.MILLISECONDS))
|
||||
.addHandlerLast(new WriteTimeoutHandler(connWriteTimeout, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
)
|
||||
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, chConnTimeout)
|
||||
.option(ChannelOption.TCP_NODELAY, chTcpNodelay)
|
||||
.option(ChannelOption.SO_KEEPALIVE, chSoKeepAlive)
|
||||
// .option(ChannelOption.ALLOCATOR, PreferHeapByteBufAllocator.DEFAULT);
|
||||
// .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
.option(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT);
|
||||
}
|
||||
);
|
||||
return WebClient.builder().exchangeStrategies(ExchangeStrategies.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build())
|
||||
.clientConnector(new ReactorClientHttpConnector(httpClient)).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return " {name=" + name +
|
||||
", maxConnections=" + maxConnections +
|
||||
", maxIdleTime=" + maxIdleTime +
|
||||
", pendingAcquireTimeout=" + pendingAcquireTimeout +
|
||||
", connReadTimeout=" + connReadTimeout +
|
||||
", connWriteTimeout=" + connWriteTimeout +
|
||||
", chConnTimeout=" + chConnTimeout +
|
||||
", chTcpNodelay=" + chTcpNodelay +
|
||||
", chSoKeepAlive=" + chSoKeepAlive +
|
||||
", compress=" + compress +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
149
src/main/java/com/wehotel/config/WebFluxConfig.java
Normal file
149
src/main/java/com/wehotel/config/WebFluxConfig.java
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.config;
|
||||
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.PreferHeapByteBufAllocator;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.reactive.ReactorResourceFactory;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
import reactor.netty.resources.ConnectionProvider;
|
||||
import reactor.netty.resources.LoopResources;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "server")
|
||||
@EnableConfigurationProperties(ServerProperties.class)
|
||||
public class WebFluxConfig {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(WebFluxConfig.class);
|
||||
|
||||
// private ConnectionProvider getConnectionProvider() {
|
||||
// String cpName = "fizz-cp";
|
||||
// ConnectionProvider cp = ConnectionProvider.builder(cpName).maxConnections(10_000)
|
||||
// .pendingAcquireTimeout(Duration.ofMillis(6_000)).maxIdleTime(Duration.ofMillis(40_000)).build();
|
||||
// log.info(cpName + ' ' + cp);
|
||||
// return cp;
|
||||
// }
|
||||
|
||||
// private LoopResources getLoopResources() {
|
||||
// String elPrefix = "fizz-el";
|
||||
// LoopResources lr = LoopResources.create(elPrefix, 1, Runtime.getRuntime().availableProcessors(), true);
|
||||
// lr.onServerSelect(false);
|
||||
// lr.onServer(false);
|
||||
// log.info("fizz-lr " + lr);
|
||||
// return lr;
|
||||
// }
|
||||
|
||||
// @Bean
|
||||
// public ReactorResourceFactory reactorResourceFactory() {
|
||||
// ReactorResourceFactory fact = new ReactorResourceFactory();
|
||||
// fact.setUseGlobalResources(false);
|
||||
// // fact.setConnectionProvider(getConnectionProvider());
|
||||
// fact.setLoopResources(getLoopResources());
|
||||
// // fact.afterPropertiesSet();
|
||||
// return fact;
|
||||
// }
|
||||
|
||||
// public static EventLoopGroup acceptorGroup;
|
||||
// public static EventLoopGroup workerGroup;
|
||||
// static {
|
||||
// if (SystemUtils.IS_OS_WINDOWS) {
|
||||
// acceptorGroup = new NioEventLoopGroup(1, new DefaultLoopResources.EventLoopFactory(true, "fizz-acceptor", new AtomicLong(0)));
|
||||
// workerGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors(), new DefaultLoopResources.EventLoopFactory(true, "fizz-worker", new AtomicLong(0)));
|
||||
// } else {
|
||||
// // DefaultLoop defaultLoop = DefaultLoopNativeDetector.getInstance();
|
||||
// // EventLoopGroup newEventLoopGroup = defaultLoop.newEventLoopGroup(
|
||||
// // selectCount,
|
||||
// // threadFactory(this, "select-" + defaultLoop.getName()));
|
||||
// acceptorGroup = new EpollEventLoopGroup(1, new DefaultLoopResources.EventLoopFactory(true, "fizz-acceptor", new AtomicLong(0)));
|
||||
// workerGroup = new EpollEventLoopGroup(Runtime.getRuntime().availableProcessors(), new DefaultLoopResources.EventLoopFactory(true, "fizz-worker", new AtomicLong(0)));
|
||||
// }
|
||||
// }
|
||||
|
||||
@Bean
|
||||
public NettyReactiveWebServerFactory nettyReactiveWebServerFactory(ServerProperties serverProperties/*, ReactorResourceFactory reactorResourceFactory*/) {
|
||||
NettyReactiveWebServerFactory httpServerFactory = new NettyReactiveWebServerFactory();
|
||||
httpServerFactory.setResourceFactory(null);
|
||||
// httpServerFactory.setResourceFactory(reactorResourceFactory);
|
||||
LoopResources lr = LoopResources.create("fizz-el", 1, Runtime.getRuntime().availableProcessors(), true);
|
||||
httpServerFactory.addServerCustomizers(
|
||||
httpServer -> (
|
||||
httpServer.tcpConfiguration(
|
||||
tcpServer -> {
|
||||
return (
|
||||
tcpServer
|
||||
// .runOn(workerGroup)
|
||||
.runOn(lr, false)
|
||||
// .runOn(lr)
|
||||
// .selectorOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
|
||||
// .port(7777)
|
||||
.bootstrap(
|
||||
serverBootstrap -> (
|
||||
serverBootstrap
|
||||
// .group(parentGroup, childGroup)
|
||||
// .channel(NioServerSocketChannel.class)
|
||||
// .handler(new LoggingHandler(LogLevel.DEBUG))
|
||||
// .childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
// @Override
|
||||
// public void initChannel(final SocketChannel socketChannel) {
|
||||
// socketChannel.pipeline().addLast(new BufferingInboundHandler());
|
||||
// }
|
||||
// })
|
||||
// .channel(NioServerSocketChannel.class)
|
||||
.option(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT)
|
||||
// .option(ChannelOption.SO_BACKLOG, 8192)
|
||||
.childOption(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT)
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||
.childOption(ChannelOption.TCP_NODELAY, true)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return httpServerFactory;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
public static class FizzWebFluxConfigurer implements WebFluxConfigurer {
|
||||
@Override
|
||||
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
|
||||
configurer.defaultCodecs().maxInMemorySize(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/main/java/com/wehotel/constants/CommonConstants.java
Normal file
44
src/main/java/com/wehotel/constants/CommonConstants.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.constants;
|
||||
|
||||
/**
|
||||
* @author unknown
|
||||
*/
|
||||
public class CommonConstants {
|
||||
|
||||
/**
|
||||
* traceId for log
|
||||
*/
|
||||
public static final String TRACE_ID = "traceId";
|
||||
|
||||
/**
|
||||
* Header key to transfer traceId
|
||||
*/
|
||||
public static final String HEADER_TRACE_ID = "X-TRACE-ID";
|
||||
|
||||
|
||||
/**
|
||||
* WildCard for PathMapping
|
||||
*/
|
||||
public static final String WILDCARD_STAR = "*";
|
||||
|
||||
|
||||
public static final String STOP_AND_RESPONSE_KEY = "_stopAndResponse";
|
||||
|
||||
}
|
||||
62
src/main/java/com/wehotel/controller/ConfigController.java
Normal file
62
src/main/java/com/wehotel/controller/ConfigController.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.controller;
|
||||
|
||||
import com.wehotel.fizz.ConfigLoader;
|
||||
import com.wehotel.util.ScriptUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author unknown
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/config")
|
||||
public class ConfigController {
|
||||
|
||||
@Resource
|
||||
private ConfigLoader configLoader;
|
||||
|
||||
@GetMapping("/reload")
|
||||
public Mono<String> reloadConfig(ServerWebExchange exchange) throws Exception {
|
||||
configLoader.init();
|
||||
return Mono.just("ok");
|
||||
}
|
||||
|
||||
// add by lancer
|
||||
@PostMapping(value = "/fullUpdCommonJs", consumes = MediaType.TEXT_PLAIN_VALUE)
|
||||
public Mono<String> fullUpdCommonJs(ServerWebExchange exchange, @RequestBody String js) {
|
||||
try {
|
||||
File file = new File("js/common.js");
|
||||
file.delete();
|
||||
file = new File("js/common.js");
|
||||
FileUtils.writeStringToFile(file, js, StandardCharsets.UTF_8);
|
||||
ScriptUtils.recreateJavascriptEngineSignalMap.clear();
|
||||
} catch (Throwable t) {
|
||||
Mono.just(t.getMessage());
|
||||
}
|
||||
return Mono.just("done");
|
||||
}
|
||||
}
|
||||
67
src/main/java/com/wehotel/controller/HealthController.java
Normal file
67
src/main/java/com/wehotel/controller/HealthController.java
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.controller;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.wehotel.plugin.auth.ApiConfigService;
|
||||
import com.wehotel.plugin.auth.AppService;
|
||||
import com.wehotel.util.JacksonUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author unknown
|
||||
*/
|
||||
@RestController
|
||||
public class HealthController {
|
||||
|
||||
@Resource
|
||||
private AppService appService;
|
||||
|
||||
@Resource
|
||||
private ApiConfigService apiConfigService;
|
||||
|
||||
@GetMapping("/time")
|
||||
public Mono<String> time(ServerWebExchange exchange) throws Exception{
|
||||
Date d = new Date();
|
||||
return Mono.just("Time: " + d.toString());
|
||||
}
|
||||
|
||||
// add by lancer
|
||||
@GetMapping("/sysgc")
|
||||
public Mono<String> sysgc(ServerWebExchange exchange) throws Exception {
|
||||
System.gc();
|
||||
return Mono.just("sysgc done");
|
||||
}
|
||||
|
||||
@GetMapping("/apps")
|
||||
public Mono<String> apps(ServerWebExchange exchange) throws Exception {
|
||||
return Mono.just(JacksonUtils.writeValueAsString(appService.getAppMap()));
|
||||
}
|
||||
|
||||
@GetMapping("/apiConfigs")
|
||||
public Mono<String> apiConfigs(ServerWebExchange exchange) throws Exception {
|
||||
return Mono.just(JacksonUtils.writeValueAsString(apiConfigService.getApp2gatewayGroupMap()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.controller;
|
||||
|
||||
import com.wehotel.controller.req.BaseManagerConfigReq;
|
||||
import com.wehotel.controller.req.GetConfigStrReq;
|
||||
import com.wehotel.controller.req.GetConfigReq;
|
||||
import com.wehotel.controller.resp.ConfigStrResp;
|
||||
import com.wehotel.controller.resp.ConfigResp;
|
||||
import com.wehotel.fizz.ConfigLoader;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 管理后台配置控制器
|
||||
* Fizz管理后台通过该控制器暴露的接口获取聚合配置相关信息
|
||||
* @author zhongjie
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/managerConfig")
|
||||
public class ManagerConfigController {
|
||||
@Value("${fizz.manager.config.key:fizz-manager-key}")
|
||||
private String key;
|
||||
|
||||
@Resource
|
||||
private ConfigLoader configLoader;
|
||||
|
||||
@PostMapping("/getAggregateConfigs")
|
||||
public Mono<ConfigResp> getAggregateConfigs(@Valid @RequestBody GetConfigReq getConfigReq) {
|
||||
this.checkSign(getConfigReq);
|
||||
String serviceNameCondition = getConfigReq.getServiceName();
|
||||
String pathCondition = getConfigReq.getPath();
|
||||
String nameCondition = getConfigReq.getName();
|
||||
List<ConfigLoader.ConfigInfo> allConfigInfoList = configLoader.getConfigInfo();
|
||||
List<ConfigLoader.ConfigInfo> configInfoList = allConfigInfoList.stream().filter(it -> {
|
||||
if (StringUtils.hasText(serviceNameCondition)) {
|
||||
if (!it.getServiceName().contains(serviceNameCondition)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
if (StringUtils.hasText(pathCondition)) {
|
||||
if (!it.getPath().contains(pathCondition)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
if (StringUtils.hasText(nameCondition)) {
|
||||
if (!it.getConfigName().contains(nameCondition)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
ConfigResp configResp = new ConfigResp();
|
||||
configResp.setConfigInfos(configInfoList);
|
||||
return Mono.just(configResp);
|
||||
}
|
||||
|
||||
@PostMapping("/getConfigStr")
|
||||
public Mono<ConfigStrResp> getConfigStr(@Valid @RequestBody GetConfigStrReq getConfigStrReq) {
|
||||
this.checkSign(getConfigStrReq);
|
||||
String configId = getConfigStrReq.getConfigId();
|
||||
String configStr = configLoader.getConfigStr(configId);
|
||||
ConfigStrResp configStrResp = new ConfigStrResp();
|
||||
configStrResp.setConfigStr(configStr);
|
||||
return Mono.just(configStrResp);
|
||||
}
|
||||
|
||||
private void checkSign(BaseManagerConfigReq req) {
|
||||
if (!req.checkSign(key)) {
|
||||
throw new RuntimeException("验证签名失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.controller.req;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 管理后台聚合配置相关请求基类
|
||||
* @author zhongjie
|
||||
*/
|
||||
public abstract class BaseManagerConfigReq implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final int MAX_TIME_DEVIATION_MINUTES = 10;
|
||||
private static final int MAX_TIME_DEVIATION_MILLIS = MAX_TIME_DEVIATION_MINUTES * 60 * 1000;
|
||||
|
||||
@NotNull(message = "签名不能为空")
|
||||
private String sign;
|
||||
@NotNull(message = "时间戳不能为空")
|
||||
private Long timestamp;
|
||||
|
||||
public boolean checkSign(String key) {
|
||||
long currentTimestamp = System.currentTimeMillis();
|
||||
if (Math.abs(currentTimestamp - timestamp) > MAX_TIME_DEVIATION_MILLIS) {
|
||||
throw new RuntimeException(String.format("请求时间戳[%s]和当前系统时间[%s]相差超过%s分钟", timestamp,
|
||||
currentTimestamp, MAX_TIME_DEVIATION_MINUTES));
|
||||
}
|
||||
return this.innerCheckSign(key, sign, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验签名
|
||||
* @param key 秘钥
|
||||
* @param sign 签名
|
||||
* @param timestamp 时间戳
|
||||
* @return TRUE:检验通过
|
||||
*/
|
||||
abstract boolean innerCheckSign(String key, String sign, Long timestamp);
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
BaseManagerConfigReq that = (BaseManagerConfigReq) o;
|
||||
return Objects.equals(sign, that.sign) &&
|
||||
Objects.equals(timestamp, that.timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(sign, timestamp);
|
||||
}
|
||||
|
||||
public String getSign() {
|
||||
return sign;
|
||||
}
|
||||
|
||||
public void setSign(String sign) {
|
||||
this.sign = sign;
|
||||
}
|
||||
|
||||
public Long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(Long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
93
src/main/java/com/wehotel/controller/req/GetConfigReq.java
Normal file
93
src/main/java/com/wehotel/controller/req/GetConfigReq.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.controller.req;
|
||||
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 获取聚合配置请求实体类
|
||||
* @author zhongjie
|
||||
*/
|
||||
public class GetConfigReq extends BaseManagerConfigReq {
|
||||
/**
|
||||
* 服务名
|
||||
*/
|
||||
private String serviceName;
|
||||
/**
|
||||
* 接口名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 接口请求路径
|
||||
*/
|
||||
private String path;
|
||||
|
||||
@Override
|
||||
boolean innerCheckSign(String key, String sign, Long timestamp) {
|
||||
return DigestUtils.md5DigestAsHex(
|
||||
String.format("%s-%s-%s-%s-%s", serviceName, name, path, timestamp, key).getBytes()).equals(sign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
GetConfigReq that = (GetConfigReq) o;
|
||||
return Objects.equals(serviceName, that.serviceName) &&
|
||||
Objects.equals(name, that.name) &&
|
||||
Objects.equals(path, that.path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), serviceName, name, path);
|
||||
}
|
||||
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
public void setServiceName(String serviceName) {
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.controller.req;
|
||||
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 获取聚合配置JSON字符串请求实体类
|
||||
* @author zhongjie
|
||||
*/
|
||||
public class GetConfigStrReq extends BaseManagerConfigReq {
|
||||
/**
|
||||
* 配置ID
|
||||
*/
|
||||
@NotBlank(message = "配置ID不能为空")
|
||||
private String configId;
|
||||
|
||||
@Override
|
||||
boolean innerCheckSign(String key, String sign, Long timestamp) {
|
||||
return DigestUtils.md5DigestAsHex(
|
||||
String.format("%s-%s-%s", configId, timestamp, key).getBytes()).equals(sign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
GetConfigStrReq that = (GetConfigStrReq) o;
|
||||
return Objects.equals(configId, that.configId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), configId);
|
||||
}
|
||||
|
||||
public String getConfigId() {
|
||||
return configId;
|
||||
}
|
||||
|
||||
public void setConfigId(String configId) {
|
||||
this.configId = configId;
|
||||
}
|
||||
}
|
||||
59
src/main/java/com/wehotel/controller/resp/ConfigResp.java
Normal file
59
src/main/java/com/wehotel/controller/resp/ConfigResp.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.controller.resp;
|
||||
|
||||
import com.wehotel.fizz.ConfigLoader;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 聚合配置响应实体类
|
||||
* @author zhongjie
|
||||
*/
|
||||
public class ConfigResp implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private List<ConfigLoader.ConfigInfo> configInfos;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ConfigResp that = (ConfigResp) o;
|
||||
return Objects.equals(configInfos, that.configInfos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(configInfos);
|
||||
}
|
||||
|
||||
public List<ConfigLoader.ConfigInfo> getConfigInfos() {
|
||||
return configInfos;
|
||||
}
|
||||
|
||||
public void setConfigInfos(List<ConfigLoader.ConfigInfo> configInfos) {
|
||||
this.configInfos = configInfos;
|
||||
}
|
||||
}
|
||||
56
src/main/java/com/wehotel/controller/resp/ConfigStrResp.java
Normal file
56
src/main/java/com/wehotel/controller/resp/ConfigStrResp.java
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.controller.resp;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 聚合配置JSON字符串响应实体类
|
||||
* @author zhongjie
|
||||
*/
|
||||
public class ConfigStrResp implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String configStr;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ConfigStrResp that = (ConfigStrResp) o;
|
||||
return Objects.equals(configStr, that.configStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(configStr);
|
||||
}
|
||||
|
||||
public String getConfigStr() {
|
||||
return configStr;
|
||||
}
|
||||
|
||||
public void setConfigStr(String configStr) {
|
||||
this.configStr = configStr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.exception;
|
||||
|
||||
/**
|
||||
* @author unknown
|
||||
*/
|
||||
public class StopAndResponseException extends RuntimeException {
|
||||
|
||||
private String data;
|
||||
|
||||
public StopAndResponseException(String message, String data) {
|
||||
super(message);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
}
|
||||
77
src/main/java/com/wehotel/filter/CorsFilterConfig.java
Normal file
77
src/main/java/com/wehotel/filter/CorsFilterConfig.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.filter;
|
||||
|
||||
import com.wehotel.config.SystemConfig;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.reactive.CorsWebFilter;
|
||||
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
@Configuration
|
||||
public class CorsFilterConfig {
|
||||
|
||||
@Resource
|
||||
private SystemConfig systemConfig;
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public CorsWebFilter corsWebFilter() {
|
||||
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
|
||||
// Possibly...
|
||||
// config.applyPermitDefaultValues()
|
||||
|
||||
config.setAllowCredentials(true);
|
||||
// config.addAllowedOrigin("https://domain0.com");
|
||||
config.addAllowedOrigin("*");
|
||||
|
||||
// config.addAllowedMethod("*");
|
||||
config.addAllowedMethod(HttpMethod.OPTIONS);
|
||||
config.addAllowedMethod(HttpMethod.GET);
|
||||
config.addAllowedMethod(HttpMethod.POST);
|
||||
config.addAllowedMethod(HttpMethod.HEAD);
|
||||
config.addAllowedMethod(HttpMethod.PUT);
|
||||
config.addAllowedMethod(HttpMethod.DELETE);
|
||||
|
||||
config.addAllowedHeader("*");
|
||||
|
||||
systemConfig.getLogHeaderSet().forEach(
|
||||
h -> {
|
||||
config.addExposedHeader(h);
|
||||
}
|
||||
);
|
||||
|
||||
config.setMaxAge(3600L);
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
|
||||
return new CorsWebFilter(source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.filter;
|
||||
|
||||
import com.wehotel.exception.StopAndResponseException;
|
||||
import com.wehotel.util.WebUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebExceptionHandler;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
@Configuration
|
||||
public class FilterExceptionHandlerConfig {
|
||||
|
||||
public static class FilterExceptionHandler implements WebExceptionHandler {
|
||||
private static final String filterExceptionHandler = "filterExceptionHandler";
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange, Throwable t) {
|
||||
if (t instanceof StopAndResponseException) {
|
||||
StopAndResponseException ex = (StopAndResponseException) t;
|
||||
if (ex.getData() != null) {
|
||||
ServerHttpResponse resp = exchange.getResponse();
|
||||
resp.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
return resp.writeWith(Mono.just(resp.bufferFactory().wrap(ex.getData().toString().getBytes())));
|
||||
}
|
||||
}
|
||||
Mono<Void> vm = WebUtils.responseError(exchange, filterExceptionHandler, HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), t);
|
||||
return vm;
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(-2)
|
||||
public FilterExceptionHandler filterExceptionHandler() {
|
||||
return new FilterExceptionHandler();
|
||||
}
|
||||
}
|
||||
68
src/main/java/com/wehotel/filter/FilterResult.java
Normal file
68
src/main/java/com/wehotel/filter/FilterResult.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.filter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
public class FilterResult {
|
||||
|
||||
public String id;
|
||||
|
||||
public boolean success;
|
||||
|
||||
public Throwable cause;
|
||||
|
||||
public Map<String, Object> data;
|
||||
|
||||
public Object get(String key) {
|
||||
return data.get(key);
|
||||
}
|
||||
|
||||
public static final FilterResult SUCCESS(String filter) {
|
||||
FilterResult r = new FilterResult();
|
||||
r.id = filter;
|
||||
r.success = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
public static final FilterResult SUCCESS_WITH(String filter, Map<String, Object> data) {
|
||||
FilterResult r = new FilterResult();
|
||||
r.id = filter;
|
||||
r.success = true;
|
||||
r.data = data;
|
||||
return r;
|
||||
}
|
||||
|
||||
public static final FilterResult FAIL(String filter) {
|
||||
FilterResult r = new FilterResult();
|
||||
r.id = filter;
|
||||
r.success = false;
|
||||
return r;
|
||||
}
|
||||
|
||||
public static final FilterResult FAIL_WITH(String filter, Throwable cause) {
|
||||
FilterResult r = new FilterResult();
|
||||
r.id = filter;
|
||||
r.success = false;
|
||||
r.cause = cause;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
143
src/main/java/com/wehotel/filter/FizzGatewayFilter.java
Normal file
143
src/main/java/com/wehotel/filter/FizzGatewayFilter.java
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.filter;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.NettyDataBufferFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.wehotel.flume.clients.log4j2appender.LogService;
|
||||
import com.wehotel.constants.CommonConstants;
|
||||
import com.wehotel.fizz.AggregateResource;
|
||||
import com.wehotel.fizz.AggregateResult;
|
||||
import com.wehotel.fizz.ConfigLoader;
|
||||
import com.wehotel.fizz.Pipeline;
|
||||
import com.wehotel.fizz.input.Input;
|
||||
import com.wehotel.util.Constants;
|
||||
import com.wehotel.util.MapUtil;
|
||||
import com.wehotel.util.WebUtils;
|
||||
|
||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
@Component
|
||||
@Order(2)
|
||||
public class FizzGatewayFilter implements WebFilter {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FizzGatewayFilter.class);
|
||||
|
||||
private static final DataBuffer emptyBody = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false, true)).wrap(Constants.Symbol.EMPTY.getBytes());
|
||||
|
||||
@Resource
|
||||
private ConfigLoader configLoader;
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
long start = System.currentTimeMillis();
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
ServerHttpResponse serverHttpResponse = exchange.getResponse();
|
||||
|
||||
String path = request.getURI().getPath();
|
||||
String method = request.getMethodValue();
|
||||
AggregateResource aggregateResource = configLoader.matchAggregateResource(method, path);
|
||||
if (aggregateResource == null) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
Pipeline pipeline = aggregateResource.getPipeline();
|
||||
Input input = aggregateResource.getInput();
|
||||
|
||||
Map<String, Object> headers = MapUtil.toHashMap(request.getHeaders());
|
||||
Map<String, Object> fizzHeaders = (Map<String, Object>) exchange.getAttributes().get(WebUtils.APPEND_HEADERS);
|
||||
if(fizzHeaders != null && !fizzHeaders.isEmpty()) {
|
||||
headers.putAll(fizzHeaders);
|
||||
}
|
||||
|
||||
// traceId
|
||||
String traceId = exchange.getRequest().getId();
|
||||
LogService.setBizId(traceId);
|
||||
serverHttpResponse.getHeaders().add(CommonConstants.HEADER_TRACE_ID, traceId);
|
||||
|
||||
// 客户端提交上来的信息
|
||||
Map<String, Object> clientInput = new HashMap<>();
|
||||
clientInput.put("path", path);
|
||||
clientInput.put("method", method);
|
||||
clientInput.put("headers", headers);
|
||||
clientInput.put("params", MapUtil.toHashMap(request.getQueryParams()));
|
||||
|
||||
|
||||
Mono<AggregateResult> result = null;
|
||||
if (HttpMethod.POST.name().equalsIgnoreCase(method)) {
|
||||
result = DataBufferUtils.join(request.getBody()).defaultIfEmpty(emptyBody).flatMap(buf -> {
|
||||
if(buf != null && buf != emptyBody) {
|
||||
try {
|
||||
clientInput.put("body", JSON.parse(buf.toString(StandardCharsets.UTF_8)));
|
||||
} finally {
|
||||
DataBufferUtils.release(buf);
|
||||
}
|
||||
}
|
||||
return pipeline.run(input, clientInput, traceId);
|
||||
});
|
||||
} else {
|
||||
result = pipeline.run(input, clientInput, traceId);
|
||||
}
|
||||
return result.subscribeOn(Schedulers.elastic()).flatMap(aggResult -> {
|
||||
LogService.setBizId(traceId);
|
||||
String jsonString = JSON.toJSONString(aggResult.getBody());
|
||||
if (aggResult.getHeaders() != null && !aggResult.getHeaders().isEmpty()) {
|
||||
aggResult.getHeaders().remove("Content-Length");
|
||||
serverHttpResponse.getHeaders().addAll(aggResult.getHeaders());
|
||||
}
|
||||
if (!serverHttpResponse.getHeaders().containsKey("Content-Type")) {
|
||||
// defalut content-type
|
||||
serverHttpResponse.getHeaders().add("Content-Type", "application/json; charset=UTF-8");
|
||||
}
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
pipeline.getStepContext().addElapsedTime("总耗时", end - start);
|
||||
LOGGER.info("ElapsedTimes={}", JSON.toJSONString(pipeline.getStepContext().getElapsedTimes()));
|
||||
|
||||
return serverHttpResponse
|
||||
.writeWith(Flux.just(exchange.getResponse().bufferFactory().wrap(jsonString.getBytes())));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
57
src/main/java/com/wehotel/filter/FizzLogFilter.java
Normal file
57
src/main/java/com/wehotel/filter/FizzLogFilter.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.filter;
|
||||
|
||||
import com.wehotel.flume.clients.log4j2appender.LogService;
|
||||
import com.wehotel.util.WebUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
@Component
|
||||
@Order(0)
|
||||
public class FizzLogFilter implements WebFilter {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FizzLogFilter.class);
|
||||
|
||||
private static final String resp = "\nresponse ";
|
||||
|
||||
private static final String in = " in ";
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
return chain.filter(exchange).doAfterTerminate(
|
||||
() -> {
|
||||
StringBuilder b = new StringBuilder(160);
|
||||
WebUtils.request2stringBuilder(exchange, b);
|
||||
b.append(resp).append(exchange.getResponse().getStatusCode())
|
||||
.append(in) .append(System.currentTimeMillis() - startTime);
|
||||
LOGGER.info(b.toString(), LogService.BIZ_ID, exchange.getRequest().getId());
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
187
src/main/java/com/wehotel/filter/PreFilter.java
Normal file
187
src/main/java/com/wehotel/filter/PreFilter.java
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.filter;
|
||||
|
||||
import com.wehotel.config.SystemConfig;
|
||||
import com.wehotel.plugin.FixedPluginFilter;
|
||||
import com.wehotel.plugin.PluginConfig;
|
||||
import com.wehotel.plugin.PluginFilter;
|
||||
import com.wehotel.plugin.auth.*;
|
||||
import com.wehotel.plugin.stat.StatPluginFilter;
|
||||
import com.wehotel.util.Constants;
|
||||
import com.wehotel.util.ReactorUtils;
|
||||
import com.wehotel.util.WebUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
@Component(PreFilter.PRE_FILTER)
|
||||
@Order(1)
|
||||
public class PreFilter extends ProxyAggrFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PreFilter.class);
|
||||
|
||||
public static final String PRE_FILTER = "preFilter";
|
||||
|
||||
private static final FilterResult succFr = FilterResult.SUCCESS(PRE_FILTER);
|
||||
|
||||
@Value("${spring.profiles.active}")
|
||||
private String profile;
|
||||
|
||||
@Value("${b-services:x}")
|
||||
private Set<String> bServices = new HashSet<>();
|
||||
|
||||
@Resource
|
||||
private SystemConfig systemConfig;
|
||||
|
||||
@Resource(name = StatPluginFilter.STAT_PLUGIN_FILTER)
|
||||
private StatPluginFilter statPluginFilter;
|
||||
|
||||
@Resource(name = AuthPluginFilter.AUTH_PLUGIN_FILTER)
|
||||
private AuthPluginFilter authPluginFilter;
|
||||
|
||||
private char currentGatewayGroup;
|
||||
@PostConstruct
|
||||
public void setCurrentGatewayGroup() {
|
||||
for (Character gg : systemConfig.getCurrentServerGatewayGroupSet()) {
|
||||
currentGatewayGroup = gg.charValue();
|
||||
log.info("current gateway group is " + currentGatewayGroup);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
|
||||
Map<String, FilterResult> fc = new HashMap<>(6, 1.0f); fc.put(WebUtils.PREV_FILTER_RESULT, succFr);
|
||||
Map<String, String> appendHdrs = new HashMap<>(6, 1.0f);
|
||||
Map<String, Object> eas = exchange.getAttributes(); eas.put(WebUtils.FILTER_CONTEXT, fc);
|
||||
eas.put(WebUtils.APPEND_HEADERS, appendHdrs);
|
||||
eas.put(WebUtils.CGG, currentGatewayGroup);
|
||||
|
||||
String app = WebUtils.getHeaderValue(exchange, WebUtils.APP_HEADER);
|
||||
if (StringUtils.isBlank(app)) {
|
||||
if (Constants.Profiles.DEV.equals(profile) || Constants.Profiles.TEST.equals(profile)) {
|
||||
String service = WebUtils.getServiceId(exchange);
|
||||
if (bServices.contains(service)) {
|
||||
app = App.TO_B;
|
||||
} else {
|
||||
app = App.TO_C;
|
||||
}
|
||||
} else if (currentGatewayGroup == GatewayGroup.B) {
|
||||
app = App.TO_B;
|
||||
} else {
|
||||
app = App.TO_C;
|
||||
}
|
||||
}
|
||||
eas.put(WebUtils.APP_HEADER, app);
|
||||
|
||||
Mono vm = statPluginFilter.filter(exchange, null, null);
|
||||
return chain(exchange, vm, authPluginFilter).defaultIfEmpty(ReactorUtils.NULL)
|
||||
.flatMap(
|
||||
v -> {
|
||||
Object authRes = WebUtils.getFilterResultDataItem(exchange, AuthPluginFilter.AUTH_PLUGIN_FILTER, AuthPluginFilter.RESULT);
|
||||
Mono m;
|
||||
if (authRes instanceof ApiConfig) {
|
||||
ApiConfig ac = (ApiConfig) authRes;
|
||||
m = executeFixedPluginFilters(exchange);
|
||||
m = m.defaultIfEmpty(ReactorUtils.NULL);
|
||||
if (ac.pluginConfigs == null || ac.pluginConfigs.isEmpty()) {
|
||||
return m.flatMap(func(exchange, chain));
|
||||
} else {
|
||||
return m.flatMap(
|
||||
e -> {
|
||||
return executeManagedPluginFilters(exchange, ac.pluginConfigs);
|
||||
}
|
||||
)
|
||||
.defaultIfEmpty(ReactorUtils.NULL).flatMap(func(exchange, chain));
|
||||
}
|
||||
} else if (authRes == ApiConfigService.Access.YES) {
|
||||
m = executeFixedPluginFilters(exchange);
|
||||
return m.defaultIfEmpty(ReactorUtils.NULL).flatMap(func(exchange, chain));
|
||||
} else {
|
||||
ApiConfigService.Access access = (ApiConfigService.Access) authRes;
|
||||
return WebUtils.responseError(exchange, HttpStatus.FORBIDDEN.value(), access.getReason());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private Mono chain(ServerWebExchange exchange, Mono m, PluginFilter pf) {
|
||||
return m.defaultIfEmpty(ReactorUtils.NULL).flatMap(
|
||||
v -> {
|
||||
return pf.filter(exchange, null, null);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private Function func(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
return v -> {
|
||||
Mono<Void> dr = WebUtils.getDirectResponse(exchange);
|
||||
if (dr != null) {
|
||||
return dr;
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
};
|
||||
}
|
||||
|
||||
private Mono<Void> executeFixedPluginFilters(ServerWebExchange exchange) {
|
||||
Mono vm = Mono.empty();
|
||||
List<FixedPluginFilter> fixedPluginFilters = FixedPluginFilter.getPluginFilters();
|
||||
for (byte i = 0; i < fixedPluginFilters.size(); i++) {
|
||||
FixedPluginFilter fpf = fixedPluginFilters.get(i);
|
||||
vm = vm.defaultIfEmpty(ReactorUtils.NULL).flatMap(
|
||||
v -> {
|
||||
return fpf.filter(exchange, null, null);
|
||||
}
|
||||
);
|
||||
}
|
||||
return vm;
|
||||
}
|
||||
|
||||
private Mono<Void> executeManagedPluginFilters(ServerWebExchange exchange, List<PluginConfig> pluginConfigs) {
|
||||
Mono vm = Mono.empty();
|
||||
ApplicationContext app = exchange.getApplicationContext();
|
||||
for (byte i = 0; i < pluginConfigs.size(); i++) {
|
||||
PluginConfig pc = pluginConfigs.get(i);
|
||||
PluginFilter pf = app.getBean(pc.plugin, PluginFilter.class);
|
||||
vm = vm.defaultIfEmpty(ReactorUtils.NULL).flatMap(
|
||||
v -> {
|
||||
return pf.filter(exchange, pc.config, pc.fixedConfig);
|
||||
}
|
||||
);
|
||||
}
|
||||
return vm;
|
||||
}
|
||||
}
|
||||
43
src/main/java/com/wehotel/filter/ProxyAggrFilter.java
Normal file
43
src/main/java/com/wehotel/filter/ProxyAggrFilter.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.filter;
|
||||
|
||||
import com.wehotel.util.WebUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
// TODO 类名调整
|
||||
public abstract class ProxyAggrFilter implements WebFilter {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
String serviceId = WebUtils.getServiceId(exchange);
|
||||
if (serviceId == null) {
|
||||
return chain.filter(exchange);
|
||||
} else {
|
||||
return doFilter(exchange, chain);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Mono<Void> doFilter(ServerWebExchange exchange, WebFilterChain chain);
|
||||
}
|
||||
140
src/main/java/com/wehotel/filter/RouteFilter.java
Normal file
140
src/main/java/com/wehotel/filter/RouteFilter.java
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.filter;
|
||||
|
||||
import com.wehotel.flume.clients.log4j2appender.LogService;
|
||||
import com.wehotel.legacy.RespEntity;
|
||||
import com.wehotel.proxy.FizzWebClient;
|
||||
import com.wehotel.util.ThreadContext;
|
||||
import com.wehotel.util.WebUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.BodyExtractors;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
@Component
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
public class RouteFilter extends ProxyAggrFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RouteFilter.class);
|
||||
|
||||
@Resource
|
||||
private FizzWebClient fizzWebClient;
|
||||
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
FilterResult pfr = WebUtils.getPrevFilterResult(exchange);
|
||||
if (pfr.success) {
|
||||
return doFilter0(exchange, chain);
|
||||
} else {
|
||||
Mono<Void> resp = WebUtils.getDirectResponse(exchange);
|
||||
if (resp == null) { // should not reach here
|
||||
ServerHttpRequest clientReq = exchange.getRequest();
|
||||
String rid = clientReq.getId();
|
||||
String msg = pfr.id + " fail";
|
||||
if (pfr.cause == null) {
|
||||
log.error(msg, LogService.BIZ_ID, rid);
|
||||
} else {
|
||||
log.error(msg, LogService.BIZ_ID, rid, pfr.cause);
|
||||
}
|
||||
return WebUtils.buildJsonDirectResponseAndBindContext(exchange, HttpStatus.OK, null, RespEntity.toJson(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg, rid));
|
||||
} else {
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Mono<Void> doFilter0(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
|
||||
ServerHttpRequest clientReq = exchange.getRequest();
|
||||
String rid = clientReq.getId();
|
||||
HttpHeaders hdrs = new HttpHeaders();
|
||||
clientReq.getHeaders().forEach(
|
||||
(h, vs) -> {
|
||||
hdrs.addAll(h, vs);
|
||||
}
|
||||
);
|
||||
Map<String, String> appendHeaders = WebUtils.getAppendHeaders(exchange);
|
||||
if (appendHeaders != null) {
|
||||
appendHeaders.forEach(
|
||||
(h, v) -> {
|
||||
List<String> vs = hdrs.get(h);
|
||||
if (vs != null && !vs.isEmpty()) {
|
||||
vs.clear();
|
||||
vs.add(v);
|
||||
} else {
|
||||
hdrs.add(h, v);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return fizzWebClient.proxySend2service(rid, clientReq.getMethod(), WebUtils.getServiceId(exchange), WebUtils.getRelativeUri(exchange), hdrs, exchange.getRequest().getBody()).flatMap(
|
||||
remoteResp -> {
|
||||
ServerHttpResponse clientResp = exchange.getResponse();
|
||||
clientResp.setStatusCode(remoteResp.statusCode());
|
||||
HttpHeaders clientRespHeaders = clientResp.getHeaders();
|
||||
HttpHeaders remoteRespHeaders = remoteResp.headers().asHttpHeaders();
|
||||
remoteRespHeaders.entrySet().forEach(
|
||||
h -> {
|
||||
String k = h.getKey();
|
||||
if (clientRespHeaders.containsKey(k)) {
|
||||
if (k.equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) || k.equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)
|
||||
|| k.equals(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS) || k.equals(HttpHeaders.ACCESS_CONTROL_MAX_AGE)) {
|
||||
} else {
|
||||
clientRespHeaders.put(k, h.getValue());
|
||||
}
|
||||
} else {
|
||||
clientRespHeaders.put(k, h.getValue());
|
||||
}
|
||||
}
|
||||
);
|
||||
if (log.isDebugEnabled()) {
|
||||
StringBuilder b = ThreadContext.getStringBuilder();
|
||||
WebUtils.response2stringBuilder(rid, remoteResp, b);
|
||||
log.debug(b.toString(), LogService.BIZ_ID, rid);
|
||||
}
|
||||
return clientResp.writeWith(remoteResp.body(BodyExtractors.toDataBuffers()))
|
||||
.doOnError(throwable -> cleanup(remoteResp)).doOnCancel(() -> cleanup(remoteResp));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void cleanup(ClientResponse clientResponse) {
|
||||
if (clientResponse != null) {
|
||||
clientResponse.bodyToMono(Void.class).subscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/main/java/com/wehotel/fizz/AggregateResource.java
Normal file
53
src/main/java/com/wehotel/fizz/AggregateResource.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.fizz;
|
||||
|
||||
import com.wehotel.fizz.input.Input;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author francis
|
||||
*
|
||||
*/
|
||||
public class AggregateResource {
|
||||
|
||||
private Pipeline pipeline;
|
||||
private Input input;
|
||||
|
||||
public AggregateResource(Pipeline pipeline, Input input) {
|
||||
super();
|
||||
this.pipeline = pipeline;
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
public Pipeline getPipeline() {
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
public void setPipeline(Pipeline pipeline) {
|
||||
this.pipeline = pipeline;
|
||||
}
|
||||
|
||||
public Input getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
public void setInput(Input input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
}
|
||||
51
src/main/java/com/wehotel/fizz/AggregateResult.java
Normal file
51
src/main/java/com/wehotel/fizz/AggregateResult.java
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author francis
|
||||
*
|
||||
*/
|
||||
public class AggregateResult {
|
||||
|
||||
private MultiValueMap<String, String> headers;
|
||||
|
||||
private Map<String, Object> body;
|
||||
|
||||
public MultiValueMap<String, String> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public void setHeaders(MultiValueMap<String, String> headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
public Map<String, Object> getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setBody(Map<String, Object> body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
}
|
||||
393
src/main/java/com/wehotel/fizz/ConfigLoader.java
Normal file
393
src/main/java/com/wehotel/fizz/ConfigLoader.java
Normal file
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.wehotel.config.AppConfigProperties;
|
||||
import com.wehotel.fizz.input.ClientInputConfig;
|
||||
import com.wehotel.fizz.input.Input;
|
||||
import com.wehotel.fizz.input.InputType;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.noear.snack.ONode;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static com.wehotel.listener.AggregateRedisConfig.AGGREGATE_REACTIVE_REDIS_TEMPLATE;
|
||||
import static com.wehotel.util.Constants.Symbol.FORWARD_SLASH;
|
||||
/**
|
||||
*
|
||||
* @author francis
|
||||
* @author zhongjie
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class ConfigLoader {
|
||||
/**
|
||||
* 聚合配置存放Hash的Key
|
||||
*/
|
||||
private static final String AGGREGATE_HASH_KEY = "fizz_aggregate_config";
|
||||
|
||||
private static Map<String, String> aggregateResources = null;
|
||||
private static Map<String, ConfigInfo> resourceKey2ConfigInfoMap = null;
|
||||
private static Map<String, String> aggregateId2ResourceKeyMap = null;
|
||||
|
||||
@Resource
|
||||
private AppConfigProperties appConfigProperties;
|
||||
@Resource(name = AGGREGATE_REACTIVE_REDIS_TEMPLATE)
|
||||
private ReactiveStringRedisTemplate reactiveStringRedisTemplate;
|
||||
|
||||
@Value("${fizz.aggregate.read-local-config-flag:false}")
|
||||
private Boolean readLocalConfigFlag;
|
||||
|
||||
public Input createInput(String configStr) throws IOException {
|
||||
ONode cfgNode = ONode.loadStr(configStr);
|
||||
|
||||
Input input = new Input();
|
||||
input.setName(cfgNode.select("$.name").getString());
|
||||
|
||||
ClientInputConfig clientInputConfig = new ClientInputConfig();
|
||||
clientInputConfig.setDataMapping(cfgNode.select("$.dataMapping").toObject(Map.class));
|
||||
clientInputConfig.setHeaders(cfgNode.select("$.headers").toObject(Map.class));
|
||||
clientInputConfig.setMethod(cfgNode.select("$.method").getString());
|
||||
clientInputConfig.setPath(cfgNode.select("$.path").getString());
|
||||
if(cfgNode.select("$.debug") != null) {
|
||||
clientInputConfig.setDebug(cfgNode.select("$.debug").getBoolean());
|
||||
}
|
||||
clientInputConfig.setType(InputType.valueOf(cfgNode.select("$.type").getString()));
|
||||
clientInputConfig.setLangDef(cfgNode.select("$.langDef").toObject(Map.class));
|
||||
clientInputConfig.setBodyDef(cfgNode.select("$.bodyDef").toObject(Map.class));
|
||||
clientInputConfig.setHeadersDef(cfgNode.select("$.headersDef").toObject(Map.class));
|
||||
clientInputConfig.setParamsDef(cfgNode.select("$.paramsDef").toObject(Map.class));
|
||||
clientInputConfig.setScriptValidate(cfgNode.select("$.scriptValidate").toObject(Map.class));
|
||||
clientInputConfig.setValidateResponse(cfgNode.select("$.validateResponse").toObject(Map.class));
|
||||
input.setConfig(clientInputConfig);
|
||||
return input;
|
||||
}
|
||||
|
||||
public Pipeline createPipeline(String configStr) throws IOException {
|
||||
ONode cfgNode = ONode.loadStr(configStr);
|
||||
Pipeline pipeline = new Pipeline();
|
||||
|
||||
List<Map<String, Object>> stepConfigs = cfgNode.select("$.stepConfigs").toObject(List.class);
|
||||
for (Map<String, Object> stepConfig : stepConfigs) {
|
||||
// set the specified env URL
|
||||
this.handleRequestURL(stepConfig);
|
||||
|
||||
Step step = new Step.Builder().read(stepConfig);
|
||||
step.setName((String) stepConfig.get("name"));
|
||||
if (stepConfig.get("stop") != null) {
|
||||
step.setStop((Boolean) stepConfig.get("stop"));
|
||||
} else {
|
||||
step.setStop(false);
|
||||
}
|
||||
step.setDataMapping((Map<String, Object>) stepConfig.get("dataMapping"));
|
||||
pipeline.addStep(step);
|
||||
}
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
public List<ConfigInfo> getConfigInfo() {
|
||||
if (aggregateResources == null) {
|
||||
try {
|
||||
this.init();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(resourceKey2ConfigInfoMap.values());
|
||||
}
|
||||
|
||||
public String getConfigStr(String configId) {
|
||||
if (aggregateResources == null) {
|
||||
try {
|
||||
this.init();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
String resourceKey = aggregateId2ResourceKeyMap.get(configId);
|
||||
if (resourceKey == null) {
|
||||
return null;
|
||||
}
|
||||
return aggregateResources.get(resourceKey);
|
||||
}
|
||||
|
||||
private void handleRequestURL(Map<String, Object> stepConfig) {
|
||||
List<Object> requests = (List<Object>) stepConfig.get("requests");
|
||||
for (Object obj : requests) {
|
||||
Map<String, Object> request = (Map<String, Object>) obj;
|
||||
String envUrl = (String) request.get(appConfigProperties.getEnv() + "Url");
|
||||
if(!StringUtils.isEmpty(envUrl)) {
|
||||
request.put("url", request.get(appConfigProperties.getEnv() + "Url"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void init() throws Exception {
|
||||
if (aggregateResources == null) {
|
||||
aggregateResources = new ConcurrentHashMap<>(1024);
|
||||
resourceKey2ConfigInfoMap = new ConcurrentHashMap<>(1024);
|
||||
aggregateId2ResourceKeyMap = new ConcurrentHashMap<>(1024);
|
||||
}
|
||||
|
||||
if (readLocalConfigFlag) {
|
||||
File dir = new File("json");
|
||||
if (dir.exists() && dir.isDirectory()) {
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null && files.length > 0) {
|
||||
for (File file : files) {
|
||||
if (!file.exists()) {
|
||||
throw new IOException("File not found");
|
||||
}
|
||||
String configStr = FileUtils.readFileToString(file, Charset.forName("UTF-8"));
|
||||
this.addConfig(configStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 从Redis缓存中获取配置
|
||||
reactiveStringRedisTemplate.opsForHash().scan(AGGREGATE_HASH_KEY).subscribe(entry -> {
|
||||
String configStr = (String) entry.getValue();
|
||||
this.addConfig(configStr);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void addConfig(String configStr) {
|
||||
if (aggregateResources == null) {
|
||||
try {
|
||||
this.init();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
ONode cfgNode = ONode.loadStr(configStr);
|
||||
String method = cfgNode.select("$.method").getString();
|
||||
String path = cfgNode.select("$.path").getString();
|
||||
String resourceKey = method.toUpperCase() + ":" + path;
|
||||
String configId = cfgNode.select("$.id").getString();
|
||||
String configName = cfgNode.select("$.name").getString();
|
||||
long version = cfgNode.select("$.version").getLong();
|
||||
if (StringUtils.hasText(configId)) {
|
||||
String existResourceKey = aggregateId2ResourceKeyMap.get(configId);
|
||||
if (StringUtils.hasText(existResourceKey)) {
|
||||
// 删除旧有的配置
|
||||
aggregateResources.remove(existResourceKey);
|
||||
resourceKey2ConfigInfoMap.remove(existResourceKey);
|
||||
}
|
||||
aggregateId2ResourceKeyMap.put(configId, resourceKey);
|
||||
}
|
||||
aggregateResources.put(resourceKey, configStr);
|
||||
resourceKey2ConfigInfoMap.put(resourceKey, this.buildConfigInfo(configId, configName, method, path, version));
|
||||
}
|
||||
|
||||
public synchronized void deleteConfig(String configIds) {
|
||||
if (CollectionUtils.isEmpty(aggregateId2ResourceKeyMap)) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSONArray idArray = JSON.parseArray(configIds);
|
||||
idArray.forEach(it -> {
|
||||
String configId = (String) it;
|
||||
String existResourceKey =aggregateId2ResourceKeyMap.get(configId);
|
||||
if (StringUtils.hasText(existResourceKey)) {
|
||||
aggregateResources.remove(existResourceKey);
|
||||
resourceKey2ConfigInfoMap.remove(existResourceKey);
|
||||
aggregateId2ResourceKeyMap.remove(configId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public AggregateResource matchAggregateResource(String method, String path) {
|
||||
if (aggregateResources == null) {
|
||||
try {
|
||||
init();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
String key = method.toUpperCase() + ":" + path;
|
||||
if(aggregateResources.containsKey(key) && aggregateResources.get(key) != null) {
|
||||
String configStr = aggregateResources.get(key);
|
||||
Input input = null;
|
||||
Pipeline pipeline = null;
|
||||
try {
|
||||
input = createInput(configStr);
|
||||
pipeline = createPipeline(configStr);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
if (pipeline != null && input != null) {
|
||||
ClientInputConfig cfg = (ClientInputConfig) input.getConfig();
|
||||
return new AggregateResource(pipeline, input);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private ConfigInfo buildConfigInfo(String configId, String configName, String method, String path, long version) {
|
||||
String serviceName = this.extractServiceName(path);
|
||||
ConfigInfo configInfo = new ConfigInfo();
|
||||
configInfo.setConfigId(configId);
|
||||
configInfo.setConfigName(configName);
|
||||
configInfo.setServiceName(serviceName);
|
||||
configInfo.setMethod(method);
|
||||
configInfo.setPath(path);
|
||||
configInfo.setVersion(version == 0 ? null : version);
|
||||
return configInfo;
|
||||
}
|
||||
|
||||
private static final String FORMAL_PATH_PREFIX = "/proxy/";
|
||||
private static final int FORMAL_PATH_SERVICE_NAME_START_INDEX = 7;
|
||||
private static final String TEST_PATH_PREFIX = "/proxytest/";
|
||||
private static final int TEST_PATH_SERVICE_NAME_START_INDEX = 11;
|
||||
private String extractServiceName(String path) {
|
||||
if (path != null) {
|
||||
if (path.startsWith(FORMAL_PATH_PREFIX)) {
|
||||
int endIndex = path.indexOf(FORWARD_SLASH, FORMAL_PATH_SERVICE_NAME_START_INDEX);
|
||||
if (endIndex > FORMAL_PATH_SERVICE_NAME_START_INDEX) {
|
||||
return path.substring(FORMAL_PATH_SERVICE_NAME_START_INDEX, endIndex);
|
||||
}
|
||||
} else if (path.startsWith(TEST_PATH_PREFIX)) {
|
||||
int endIndex = path.indexOf(FORWARD_SLASH, TEST_PATH_SERVICE_NAME_START_INDEX);
|
||||
if (endIndex > TEST_PATH_SERVICE_NAME_START_INDEX) {
|
||||
return path.substring(TEST_PATH_SERVICE_NAME_START_INDEX, endIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class ConfigInfo implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* 配置ID
|
||||
*/
|
||||
private String configId;
|
||||
|
||||
/**
|
||||
* 配置名
|
||||
*/
|
||||
private String configName;
|
||||
|
||||
/**
|
||||
* 服务名
|
||||
*/
|
||||
private String serviceName;
|
||||
/**
|
||||
* 接口请求method类型
|
||||
*/
|
||||
private String method;
|
||||
/**
|
||||
* 接口请求路径
|
||||
*/
|
||||
private String path;
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
private Long version;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ConfigInfo that = (ConfigInfo) o;
|
||||
return Objects.equals(configId, that.configId) &&
|
||||
Objects.equals(configName, that.configName) &&
|
||||
Objects.equals(serviceName, that.serviceName) &&
|
||||
Objects.equals(method, that.method) &&
|
||||
Objects.equals(path, that.path) &&
|
||||
Objects.equals(version, that.version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(configId, configName, serviceName, method, path, version);
|
||||
}
|
||||
|
||||
public String getConfigId() {
|
||||
return configId;
|
||||
}
|
||||
|
||||
public void setConfigId(String configId) {
|
||||
this.configId = configId;
|
||||
}
|
||||
|
||||
public String getConfigName() {
|
||||
return configName;
|
||||
}
|
||||
|
||||
public void setConfigName(String configName) {
|
||||
this.configName = configName;
|
||||
}
|
||||
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
public void setServiceName(String serviceName) {
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
351
src/main/java/com/wehotel/fizz/Pipeline.java
Normal file
351
src/main/java/com/wehotel/fizz/Pipeline.java
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import com.wehotel.fizz.input.ClientInputConfig;
|
||||
import com.wehotel.fizz.input.InputConfig;
|
||||
import com.wehotel.schema.util.I18nUtils;
|
||||
import com.wehotel.util.JsonSchemaUtils;
|
||||
|
||||
import org.noear.snack.ONode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.wehotel.flume.clients.log4j2appender.LogService;
|
||||
import com.wehotel.fizz.input.Input;
|
||||
import com.wehotel.fizz.input.PathMapping;
|
||||
import com.wehotel.fizz.input.ScriptHelper;
|
||||
import com.wehotel.util.MapUtil;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
* @author francis
|
||||
* @author zhongjie
|
||||
*
|
||||
*/
|
||||
public class Pipeline {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Pipeline.class);
|
||||
private LinkedList<Step> steps = new LinkedList<Step>();
|
||||
private StepContext<String, Object> stepContext= new StepContext<>();
|
||||
public void addStep(Step step) {
|
||||
steps.add(step);
|
||||
}
|
||||
|
||||
static void displayValue(String n) {
|
||||
System.out.println("input : " + n);
|
||||
}
|
||||
|
||||
public StepContext<String, Object> getStepContext(){
|
||||
return this.stepContext;
|
||||
}
|
||||
|
||||
public Mono<AggregateResult> run(Input input, Map<String, Object> clientInput, String traceId) {
|
||||
ClientInputConfig config = (ClientInputConfig)input.getConfig();
|
||||
this.initialStepContext(clientInput);
|
||||
this.stepContext.setDebug(config.isDebug());
|
||||
|
||||
if(traceId != null) {
|
||||
this.stepContext.setTraceId(traceId);
|
||||
}
|
||||
|
||||
long t1 = System.currentTimeMillis();
|
||||
List<String> validateErrorList = inputValidate(input, clientInput);
|
||||
this.stepContext.addElapsedTime("入参校验", System.currentTimeMillis()-t1);
|
||||
|
||||
if (!CollectionUtils.isEmpty(validateErrorList)) {
|
||||
long t2 = System.currentTimeMillis();
|
||||
String validateMsg = StringUtils.collectionToCommaDelimitedString(validateErrorList);
|
||||
// 将验证错误信息放入上下文
|
||||
stepContext.put("validateMsg", validateMsg);
|
||||
Map<String, Object> validateResponse = config.getValidateResponse();
|
||||
// 数据转换
|
||||
AggregateResult aggregateResult = this.doInputDataMapping(input, validateResponse);
|
||||
this.stepContext.addElapsedTime("入参校验结果转换", System.currentTimeMillis() - t2);
|
||||
return Mono.just(aggregateResult);
|
||||
}
|
||||
|
||||
LinkedList<Step> opSteps = (LinkedList<Step>) steps.clone();
|
||||
Step step1 = opSteps.removeFirst();
|
||||
step1.beforeRun(stepContext, null);
|
||||
Mono<List<StepResponse>> result = createStep(step1).expand(response -> {
|
||||
if (opSteps.isEmpty() || response.isStop()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
Step step = opSteps.pop();
|
||||
step.beforeRun(stepContext, response);
|
||||
return createStep(step);
|
||||
}).flatMap(response -> Flux.just(response)).collectList();
|
||||
return result.flatMap(clientResponse -> {
|
||||
// 数据转换
|
||||
long t3 = System.currentTimeMillis();
|
||||
AggregateResult aggResult = this.doInputDataMapping(input, null);
|
||||
this.stepContext.addElapsedTime(input.getName()+"聚合接口响应结果数据转换", System.currentTimeMillis() - t3);
|
||||
if(this.stepContext.isDebug()) {
|
||||
LogService.setBizId(this.stepContext.getTraceId());
|
||||
String jsonString = JSON.toJSONString(aggResult);
|
||||
LOGGER.info("aggResult {}", jsonString);
|
||||
LOGGER.info("stepContext {}", JSON.toJSONString(stepContext));
|
||||
}
|
||||
return Mono.just(aggResult);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<StepResponse> createStep(Step step) {
|
||||
long start = System.currentTimeMillis();
|
||||
List<Mono> monos = step.run();
|
||||
Mono<Map>[] monoArray = monos.stream().toArray(Mono[]::new);
|
||||
Mono<StepResponse>result = Flux.merge(monoArray).reduce(new HashMap(), (item1, item2)-> {
|
||||
Input input = (Input)item2.get("request");
|
||||
item1.put(input.getName() , item2.get("data"));
|
||||
return item1;
|
||||
}).flatMap(item -> {
|
||||
// stepResult 数据转换
|
||||
long t1 = System.currentTimeMillis();
|
||||
StepResponse stepResponse = this.doStepDataMapping(step);
|
||||
stepResponse.setStop(step.isStop());
|
||||
long t2 = System.currentTimeMillis();
|
||||
this.stepContext.addElapsedTime(step.getName() + "结果数据转换", t2 - t1);
|
||||
this.stepContext.addElapsedTime(step.getName() + "耗时", System.currentTimeMillis() - start);
|
||||
|
||||
return Mono.just(stepResponse);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化上下文
|
||||
* @param clientInput 客户端提交上来的信息
|
||||
*/
|
||||
public void initialStepContext(Map<String,Object> clientInput) {
|
||||
Map<String,Object> input = new HashMap<>();
|
||||
Map<String,Object> inputRequest = new HashMap<>();
|
||||
Map<String,Object> inputResponse = new HashMap<>();
|
||||
input.put("request", inputRequest);
|
||||
input.put("response", inputResponse);
|
||||
if(clientInput != null) {
|
||||
inputRequest.put("path", clientInput.get("path"));
|
||||
inputRequest.put("method", clientInput.get("method"));
|
||||
inputRequest.put("headers", clientInput.get("headers"));
|
||||
inputRequest.put("params", clientInput.get("params"));
|
||||
inputRequest.put("body", clientInput.get("body"));
|
||||
}
|
||||
stepContext.put("input", input);
|
||||
}
|
||||
|
||||
|
||||
private StepResponse doStepDataMapping(Step step) {
|
||||
StepResponse stepResponse = (StepResponse)stepContext.get(step.getName());
|
||||
if (step.getDataMapping() != null) {
|
||||
Map<String, Object> responseMapping = (Map<String, Object>) step.getDataMapping().get("response");
|
||||
if(responseMapping != null && !StringUtils.isEmpty(responseMapping)) {
|
||||
ONode ctxNode = PathMapping.toONode(stepContext);
|
||||
|
||||
// body
|
||||
stepResponse.setResult(PathMapping.transform(ctxNode, stepContext,
|
||||
(Map<String, Object>) responseMapping.get("fixedBody"),
|
||||
(Map<String, Object>) responseMapping.get("body")));
|
||||
|
||||
// script
|
||||
if(responseMapping.get("script") != null) {
|
||||
Map<String, Object> scriptCfg = (Map<String, Object>) responseMapping.get("script");
|
||||
try {
|
||||
Map<String, Object> stepBody = ScriptHelper.execute(scriptCfg, ctxNode, stepContext, Map.class);
|
||||
if(stepBody != null) {
|
||||
stepResponse.setResult(stepBody);
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
LOGGER.warn("execute script failed, {}", e);
|
||||
throw new RuntimeException("execute script failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stepResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当validateResponse不为空表示验参失败,使用该配置响应数据
|
||||
*/
|
||||
private AggregateResult doInputDataMapping(Input input, Map<String, Object> validateResponse) {
|
||||
AggregateResult aggResult = new AggregateResult();
|
||||
Map<String, Map<String,Object>> group = (Map<String, Map<String, Object>>) stepContext.get("input");
|
||||
if(group == null) {
|
||||
group = new HashMap<String, Map<String,Object>>();
|
||||
stepContext.put("input", group);
|
||||
}
|
||||
Map<String,Object> response = null;
|
||||
if(group.get("response") == null) {
|
||||
response = new HashMap<>();
|
||||
group.put("response", response);
|
||||
}
|
||||
response = group.get("response");
|
||||
if (input != null && input.getConfig() != null && input.getConfig().getDataMapping() != null) {
|
||||
Map<String, Object> responseMapping = (Map<String, Object>) input.getConfig().getDataMapping()
|
||||
.get("response");
|
||||
if (validateResponse != null) {
|
||||
responseMapping = validateResponse;
|
||||
}
|
||||
if (responseMapping != null && !StringUtils.isEmpty(responseMapping)) {
|
||||
ONode ctxNode = PathMapping.toONode(stepContext);
|
||||
|
||||
// headers
|
||||
response.put("headers",
|
||||
PathMapping.transform(ctxNode, stepContext,
|
||||
(Map<String, Object>) responseMapping.get("fixedHeaders"),
|
||||
(Map<String, Object>) responseMapping.get("headers")));
|
||||
|
||||
// body
|
||||
Map<String,Object> body = PathMapping.transform(ctxNode, stepContext,
|
||||
(Map<String, Object>) responseMapping.get("fixedBody"),
|
||||
(Map<String, Object>) responseMapping.get("body"));
|
||||
|
||||
// script
|
||||
if (responseMapping.get("script") != null) {
|
||||
Map<String, Object> scriptCfg = (Map<String, Object>) responseMapping.get("script");
|
||||
try {
|
||||
Object respBody = ScriptHelper.execute(scriptCfg, ctxNode, stepContext);
|
||||
if(respBody != null) {
|
||||
body.putAll((Map<String, Object>) respBody);
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
LOGGER.warn("execute script failed, {}", e);
|
||||
throw new RuntimeException("execute script failed");
|
||||
}
|
||||
}
|
||||
response.put("body", body);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> respBody = (Map<String, Object>) response.get("body");
|
||||
// 测试模式返回StepContext
|
||||
if(stepContext.returnContext()) {
|
||||
respBody.put("_context", stepContext);
|
||||
}
|
||||
|
||||
aggResult.setBody((Map<String, Object>) response.get("body"));
|
||||
aggResult.setHeaders(MapUtil.toMultiValueMap((Map<String, Object>) response.get("headers")));
|
||||
return aggResult;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<String> inputValidate(Input input, Map<String, Object> clientInput) {
|
||||
try {
|
||||
InputConfig config = input.getConfig();
|
||||
if (config instanceof ClientInputConfig) {
|
||||
Map<String, Object> langDef = ((ClientInputConfig) config).getLangDef();
|
||||
this.handleLangDef(langDef);
|
||||
|
||||
Map<String, Object> headersDef = ((ClientInputConfig) config).getHeadersDef();
|
||||
if (!CollectionUtils.isEmpty(headersDef)) {
|
||||
// 验证headers入参是否符合要求
|
||||
List<String> errorList = JsonSchemaUtils.validateAllowValueStr(JSON.toJSONString(headersDef), JSON.toJSONString(clientInput.get("headers")));
|
||||
if (!CollectionUtils.isEmpty(errorList)) {
|
||||
return errorList;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> paramsDef = ((ClientInputConfig) config).getParamsDef();
|
||||
if (!CollectionUtils.isEmpty(paramsDef)) {
|
||||
// 验证params入参是否符合要求
|
||||
List<String> errorList = JsonSchemaUtils.validateAllowValueStr(JSON.toJSONString(paramsDef), JSON.toJSONString(clientInput.get("params")));
|
||||
if (!CollectionUtils.isEmpty(errorList)) {
|
||||
return errorList;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> bodyDef = ((ClientInputConfig) config).getBodyDef();
|
||||
if (!CollectionUtils.isEmpty(bodyDef)) {
|
||||
// 验证body入参是否符合要求
|
||||
List<String> errorList = JsonSchemaUtils.validate(JSON.toJSONString(bodyDef), JSON.toJSONString(clientInput.get("body")));
|
||||
if (!CollectionUtils.isEmpty(errorList)) {
|
||||
return errorList;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> scriptValidate = ((ClientInputConfig) config).getScriptValidate();
|
||||
if (!CollectionUtils.isEmpty(scriptValidate)) {
|
||||
ONode ctxNode = PathMapping.toONode(stepContext);
|
||||
// 验证入参是否符合脚本要求
|
||||
try {
|
||||
List<String> errorList = (List<String>) ScriptHelper.execute(scriptValidate, ctxNode, stepContext, List.class);
|
||||
if (!CollectionUtils.isEmpty(errorList)) {
|
||||
return errorList;
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
LOGGER.warn("execute script failed", e);
|
||||
throw new RuntimeException("execute script failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
I18nUtils.removeContextLocale();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void handleLangDef(Map<String, Object> langDef) {
|
||||
if (!CollectionUtils.isEmpty(langDef)) {
|
||||
// 存在提示语言定义配置
|
||||
Object langParamObj = langDef.get("langParam");
|
||||
String langParam = null;
|
||||
if (langParamObj instanceof String) {
|
||||
langParam = (String) langParamObj;
|
||||
}
|
||||
Object langMappingObj = langDef.get("langMapping");
|
||||
Map<String, Object> langMapping = null;
|
||||
if (langMappingObj instanceof Map) {
|
||||
langMapping = (Map<String, Object>) langMappingObj;
|
||||
}
|
||||
if (langParam != null && !CollectionUtils.isEmpty(langMapping)) {
|
||||
ONode ctxNode = PathMapping.toONode(stepContext);
|
||||
Map<String, Object> langParamMap = new HashMap<>(2);
|
||||
langParamMap.put("langParam", langParam);
|
||||
Map<String, Object> transformMap = PathMapping.transform(ctxNode, stepContext, null, langParamMap);
|
||||
Object langParamValue = transformMap.get("langParam");
|
||||
if (langParamValue != null) {
|
||||
// 判断使用哪种语言
|
||||
Object zh = langMapping.get("zh");
|
||||
if (zh != null && zh.toString().equals(langParamValue.toString())) {
|
||||
I18nUtils.setContextLocale(new Locale("zh"));
|
||||
}
|
||||
Object en = langMapping.get("en");
|
||||
if (en != null && en.toString().equals(langParamValue.toString())) {
|
||||
I18nUtils.setContextLocale(new Locale("en"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
148
src/main/java/com/wehotel/fizz/Step.java
Normal file
148
src/main/java/com/wehotel/fizz/Step.java
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||
import org.springframework.web.reactive.function.BodyInserter;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.wehotel.fizz.input.Input;
|
||||
import com.wehotel.fizz.input.InputConfig;
|
||||
import com.wehotel.fizz.input.InputContext;
|
||||
import com.wehotel.fizz.input.InputFactory;
|
||||
import com.wehotel.fizz.input.InputType;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
* @author francis
|
||||
*
|
||||
*/
|
||||
public class Step {
|
||||
|
||||
private String name;
|
||||
|
||||
// 是否在执行完当前step就返回
|
||||
private boolean stop;
|
||||
|
||||
private Map<String, Object> dataMapping;
|
||||
|
||||
private Map<String, InputConfig> requestConfigs = new HashMap<String, InputConfig>();
|
||||
|
||||
public static class Builder {
|
||||
public Step read(Map<String, Object> config) {
|
||||
Step step = new Step();
|
||||
List<Map> requests= (List<Map>) config.get("requests");
|
||||
for(Map requestConfig: requests) {
|
||||
InputConfig inputConfig = InputFactory.createInputConfig(requestConfig);
|
||||
step.addRequestConfig((String)requestConfig.get("name"), inputConfig);
|
||||
}
|
||||
return step;
|
||||
}
|
||||
}
|
||||
|
||||
private StepContext<String, Object> stepContext;
|
||||
private StepResponse lastStepResponse = null;
|
||||
private Map<String, Input> inputs = new HashMap<String, Input>();
|
||||
public void beforeRun(StepContext<String, Object> stepContext2, StepResponse response ) {
|
||||
stepContext = stepContext2;
|
||||
lastStepResponse = response;
|
||||
StepResponse stepResponse = new StepResponse(this, null, new HashMap<String, Map<String, Object>>());
|
||||
stepContext.put(name, stepResponse);
|
||||
Map<String, InputConfig> configs = this.getRequestConfigs();
|
||||
for(String configName :configs.keySet()) {
|
||||
InputConfig inputConfig = configs.get(configName);
|
||||
InputType type = inputConfig.getType();
|
||||
Input input = InputFactory.createInput(type.toString());
|
||||
input.setConfig(inputConfig);
|
||||
input.setName(configName);
|
||||
input.setStepResponse(stepResponse);
|
||||
InputContext context = new InputContext(stepContext, lastStepResponse);
|
||||
input.beforeRun(context);
|
||||
inputs.put(input.getName(), input);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Mono> run() {
|
||||
List<Mono> monos = new ArrayList<Mono>();
|
||||
for(String name :inputs.keySet()) {
|
||||
Input input = inputs.get(name);
|
||||
if (input.needRun(stepContext)) {
|
||||
Mono<Map> singleMono = input.run();
|
||||
monos.add(singleMono);
|
||||
}
|
||||
}
|
||||
return monos;
|
||||
}
|
||||
|
||||
public void afeterRun() {
|
||||
|
||||
}
|
||||
|
||||
public InputConfig addRequestConfig(String name, InputConfig requestConfig) {
|
||||
return requestConfigs.put(name, requestConfig);
|
||||
}
|
||||
|
||||
|
||||
public Map<String, InputConfig> getRequestConfigs() {
|
||||
return requestConfigs;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
if (name == null) {
|
||||
return name = "step" + (int)(Math.random()*100);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isStop() {
|
||||
return stop;
|
||||
}
|
||||
|
||||
public void setStop(boolean stop) {
|
||||
this.stop = stop;
|
||||
}
|
||||
|
||||
public Map<String, Object> getDataMapping() {
|
||||
return dataMapping;
|
||||
}
|
||||
|
||||
public void setDataMapping(Map<String, Object> dataMapping) {
|
||||
this.dataMapping = dataMapping;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
549
src/main/java/com/wehotel/fizz/StepContext.java
Normal file
549
src/main/java/com/wehotel/fizz/StepContext.java
Normal file
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.wehotel.constants.CommonConstants;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
* @author francis
|
||||
*
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class StepContext<K, V> extends ConcurrentHashMap<K, V> {
|
||||
|
||||
public static final String ELAPSED_TIMES = "elapsedTimes";
|
||||
public static final String DEBUG = "debug";
|
||||
public static final String RETURN_CONTEXT = "returnContext";
|
||||
|
||||
public void setDebug(Boolean debug) {
|
||||
this.put((K)DEBUG, (V)debug);
|
||||
}
|
||||
|
||||
public String getTraceId() {
|
||||
return (String) this.get(CommonConstants.TRACE_ID);
|
||||
}
|
||||
|
||||
public void setTraceId(String traceId) {
|
||||
this.put((K)CommonConstants.TRACE_ID, (V)traceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否调试模式
|
||||
* @return
|
||||
*/
|
||||
public Boolean isDebug() {
|
||||
return this.get(DEBUG) == null ? false : (Boolean) this.get(DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否在响应体里返回上下文
|
||||
* @return
|
||||
*/
|
||||
public boolean returnContext() {
|
||||
return Boolean.valueOf((String)getInputReqHeader(RETURN_CONTEXT));
|
||||
}
|
||||
|
||||
public synchronized void addElapsedTime(String actionName, Long milliSeconds) {
|
||||
List<Map<String, Long>> elapsedTimes = (List<Map<String, Long>>) this.get(ELAPSED_TIMES);
|
||||
if (elapsedTimes == null) {
|
||||
elapsedTimes = new ArrayList<Map<String, Long>>();
|
||||
this.put((K) ELAPSED_TIMES, (V) elapsedTimes);
|
||||
}
|
||||
Map<String, Long> record = new HashMap<>();
|
||||
record.put(actionName, milliSeconds);
|
||||
elapsedTimes.add(record);
|
||||
}
|
||||
|
||||
public V getElapsedTimes() {
|
||||
return this.get(ELAPSED_TIMES);
|
||||
}
|
||||
|
||||
private Map<String, Object> getStepRequest(String stepName, String requestName) {
|
||||
StepResponse stepResponse = (StepResponse) this.get(stepName);
|
||||
if (stepResponse == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Map<String, Object>> requests = (Map<String, Map<String, Object>>) stepResponse.getRequests();
|
||||
if (requests == null) {
|
||||
requests = new HashMap<>();
|
||||
stepResponse.setRequests(requests);
|
||||
requests.put(requestName, new HashMap<String, Object>());
|
||||
}
|
||||
return (Map<String, Object>) requests.get(requestName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Step里调用接口的请求头
|
||||
*
|
||||
* @param stepName
|
||||
* @param requestName
|
||||
* @param headerName
|
||||
* @param headerValue
|
||||
*/
|
||||
public void setStepReqHeader(String stepName, String requestName, String headerName, Object headerValue) {
|
||||
Map<String, Object> request = getStepRequest(stepName, requestName);
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> req = (Map<String, Object>) request.get("request");
|
||||
if (req == null) {
|
||||
req = new HashMap<>();
|
||||
request.put("request", req);
|
||||
}
|
||||
Map<String, Object> headers = (Map<String, Object>) req.get("headers");
|
||||
if (headers == null) {
|
||||
headers = new HashMap<>();
|
||||
req.put("headers", headers);
|
||||
}
|
||||
headers.put(headerName, headerValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Step里调用接口的请求头
|
||||
*
|
||||
* @param stepName
|
||||
* @param requestName
|
||||
* @param headerName
|
||||
*/
|
||||
public Object getStepReqHeader(String stepName, String requestName, String headerName) {
|
||||
Map<String, Object> request = getStepRequest(stepName, requestName);
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> req = (Map<String, Object>) request.get("request");
|
||||
if (req == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> headers = (Map<String, Object>) req.get("headers");
|
||||
if (headers == null) {
|
||||
return null;
|
||||
}
|
||||
return headers.get(headerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Step里调用接口的请求body
|
||||
*
|
||||
* @param stepName
|
||||
* @param requestName
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setStepReqBody(String stepName, String requestName, String key, Object value) {
|
||||
Map<String, Object> request = getStepRequest(stepName, requestName);
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> req = (Map<String, Object>) request.get("request");
|
||||
if (req == null) {
|
||||
req = new HashMap<>();
|
||||
request.put("request", req);
|
||||
}
|
||||
Map<String, Object> body = (Map<String, Object>) req.get("body");
|
||||
if (body == null) {
|
||||
body = new HashMap<>();
|
||||
req.put("body", body);
|
||||
}
|
||||
body.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Step里调用接口的请求body
|
||||
*
|
||||
* @param stepName
|
||||
* @param requestName
|
||||
* @param key
|
||||
*/
|
||||
public Object getStepReqBody(String stepName, String requestName, String key) {
|
||||
Map<String, Object> request = getStepRequest(stepName, requestName);
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> req = (Map<String, Object>) request.get("request");
|
||||
if (req == null) {
|
||||
req = new HashMap<>();
|
||||
request.put("request", req);
|
||||
}
|
||||
Map<String, Object> body = (Map<String, Object>) req.get("body");
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
return body.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Step里调用接口的请求body
|
||||
*
|
||||
* @param stepName
|
||||
* @param requestName
|
||||
*/
|
||||
public Object getStepReqBody(String stepName, String requestName) {
|
||||
Map<String, Object> request = getStepRequest(stepName, requestName);
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> req = (Map<String, Object>) request.get("request");
|
||||
if (req == null) {
|
||||
req = new HashMap<>();
|
||||
request.put("request", req);
|
||||
}
|
||||
return req.get("body");
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Step里调用接口响应头
|
||||
*
|
||||
* @param stepName
|
||||
* @param requestName
|
||||
* @param headerName
|
||||
* @param headerValue
|
||||
*/
|
||||
public void setStepRespHeader(String stepName, String requestName, String headerName, Object headerValue) {
|
||||
Map<String, Object> request = getStepRequest(stepName, requestName);
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> response = (Map<String, Object>) request.get("response");
|
||||
if (response == null) {
|
||||
response = new HashMap<>();
|
||||
request.put("response", response);
|
||||
}
|
||||
Map<String, Object> headers = (Map<String, Object>) response.get("headers");
|
||||
if (headers == null) {
|
||||
headers = new HashMap<>();
|
||||
response.put("headers", headers);
|
||||
}
|
||||
headers.put(headerName, headerValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Step里调用接口响应头
|
||||
*
|
||||
* @param stepName
|
||||
* @param requestName
|
||||
* @param headerName
|
||||
*/
|
||||
public Object getStepRespHeader(String stepName, String requestName, String headerName) {
|
||||
Map<String, Object> request = getStepRequest(stepName, requestName);
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> response = (Map<String, Object>) request.get("response");
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> headers = (Map<String, Object>) response.get("headers");
|
||||
if (headers == null) {
|
||||
return null;
|
||||
}
|
||||
return headers.get(headerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Step里调用接口的响应body
|
||||
*
|
||||
* @param stepName
|
||||
* @param requestName
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setStepRespBody(String stepName, String requestName, String key, Object value) {
|
||||
Map<String, Object> request = getStepRequest(stepName, requestName);
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> response = (Map<String, Object>) request.get("response");
|
||||
if (response == null) {
|
||||
response = new HashMap<>();
|
||||
request.put("response", response);
|
||||
}
|
||||
Map<String, Object> body = (Map<String, Object>) response.get("body");
|
||||
if (body == null) {
|
||||
body = new HashMap<>();
|
||||
response.put("body", body);
|
||||
}
|
||||
body.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Step里调用接口的响应body
|
||||
*
|
||||
* @param stepName
|
||||
* @param requestName
|
||||
* @param key
|
||||
*/
|
||||
public Object getStepRespBody(String stepName, String requestName, String key) {
|
||||
Map<String, Object> request = getStepRequest(stepName, requestName);
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> response = (Map<String, Object>) request.get("response");
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> body = (Map<String, Object>) response.get("body");
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
return body.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Step里调用接口的响应body
|
||||
*
|
||||
* @param stepName
|
||||
* @param requestName
|
||||
*/
|
||||
public Object getStepRespBody(String stepName, String requestName) {
|
||||
Map<String, Object> request = getStepRequest(stepName, requestName);
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> response = (Map<String, Object>) request.get("response");
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
return response.get("body");
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Step的结果
|
||||
*
|
||||
* @param stepName
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setStepResult(String stepName, String key, Object value) {
|
||||
StepResponse stepResponse = (StepResponse) this.get(stepName);
|
||||
if (stepResponse == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> result = (Map<String, Object>) stepResponse.getResult();
|
||||
if (result == null) {
|
||||
result = new HashMap<>();
|
||||
stepResponse.setResult(result);
|
||||
}
|
||||
result.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Step的结果
|
||||
*
|
||||
* @param stepName
|
||||
* @param key
|
||||
*/
|
||||
public Object getStepResult(String stepName, String key) {
|
||||
StepResponse stepResponse = (StepResponse) this.get(stepName);
|
||||
if (stepResponse == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> result = (Map<String, Object>) stepResponse.getResult();
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
return result.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Step的结果
|
||||
*
|
||||
* @param stepName
|
||||
*/
|
||||
public Object getStepResult(String stepName) {
|
||||
StepResponse stepResponse = (StepResponse) this.get(stepName);
|
||||
if (stepResponse == null) {
|
||||
return null;
|
||||
}
|
||||
return stepResponse.getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置聚合接口的响应头
|
||||
*
|
||||
* @param headerName
|
||||
* @param headerValue
|
||||
*/
|
||||
public void setInputRespHeader(String headerName, Object headerValue) {
|
||||
Map<String, Object> input = (Map<String, Object>) this.get("input");
|
||||
if (input == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> response = (Map<String, Object>) input.get("response");
|
||||
if (response == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> headers = (Map<String, Object>) response.get("headers");
|
||||
if (headers == null) {
|
||||
headers = new HashMap<>();
|
||||
response.put("headers", headers);
|
||||
}
|
||||
headers.put(headerName, headerValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聚合接口的响应头
|
||||
*
|
||||
* @param headerName
|
||||
*/
|
||||
public Object getInputRespHeader(String headerName) {
|
||||
Map<String, Object> input = (Map<String, Object>) this.get("input");
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> response = (Map<String, Object>) input.get("response");
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> headers = (Map<String, Object>) response.get("headers");
|
||||
if (headers == null) {
|
||||
return null;
|
||||
}
|
||||
return headers.get(headerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聚合接口的请求头
|
||||
*
|
||||
* @param headerName
|
||||
*/
|
||||
public Object getInputReqHeader(String headerName) {
|
||||
Map<String, Object> input = (Map<String, Object>) this.get("input");
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> request = (Map<String, Object>) input.get("request");
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> headers = (Map<String, Object>) request.get("headers");
|
||||
if (headers == null) {
|
||||
return null;
|
||||
}
|
||||
return headers.get(headerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置聚合接口的响应body
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setInputRespBody(String key, Object value) {
|
||||
Map<String, Object> input = (Map<String, Object>) this.get("input");
|
||||
if (input == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> response = (Map<String, Object>) input.get("response");
|
||||
if (response == null) {
|
||||
response = new HashMap<>();
|
||||
input.put("response", response);
|
||||
}
|
||||
Map<String, Object> body = (Map<String, Object>) response.get("body");
|
||||
if (body == null) {
|
||||
body = new HashMap<>();
|
||||
response.put("body", body);
|
||||
}
|
||||
body.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聚合接口的响应body
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public Object getInputRespBody(String key) {
|
||||
Map<String, Object> input = (Map<String, Object>) this.get("input");
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> response = (Map<String, Object>) input.get("response");
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> body = (Map<String, Object>) response.get("body");
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
return body.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聚合接口的响应body
|
||||
*
|
||||
*/
|
||||
public Object getInputRespBody() {
|
||||
Map<String, Object> input = (Map<String, Object>) this.get("input");
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> response = (Map<String, Object>) input.get("response");
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
return response.get("body");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聚合接口的请求body
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public Object getInputReqBody(String key) {
|
||||
Map<String, Object> body = (Map<String, Object>) getInputReqAttr("body");
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
return body.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聚合接口的请求body
|
||||
*
|
||||
*/
|
||||
public Object getInputReqBody() {
|
||||
return getInputReqAttr("body");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聚合接口请求属性<br/>
|
||||
* 可选属性:path,method,headers,params,body
|
||||
*
|
||||
*/
|
||||
public Object getInputReqAttr(String key) {
|
||||
Map<String, Object> input = (Map<String, Object>) this.get("input");
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> request = (Map<String, Object>) input.get("request");
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
return request.get(key);
|
||||
}
|
||||
|
||||
}
|
||||
67
src/main/java/com/wehotel/fizz/StepResponse.java
Normal file
67
src/main/java/com/wehotel/fizz/StepResponse.java
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author linwaiwai
|
||||
*/
|
||||
public class StepResponse {
|
||||
private String stepName;
|
||||
private Map<String, Map<String, Object>> requests;
|
||||
private Map result;
|
||||
private boolean stop;
|
||||
|
||||
public StepResponse(Step aStep, HashMap item, Map<String, Map<String, Object>> requests) {
|
||||
setStepName(aStep.getName());
|
||||
setResult(item);
|
||||
setRequests(requests);
|
||||
}
|
||||
public StepResponse(Step aStep, HashMap item) {
|
||||
setStepName(aStep.getName());
|
||||
setResult(item);
|
||||
}
|
||||
|
||||
public boolean isStop() {
|
||||
return stop;
|
||||
}
|
||||
public void setStop(boolean stop) {
|
||||
this.stop = stop;
|
||||
}
|
||||
public String getStepName() {
|
||||
return stepName;
|
||||
}
|
||||
public void setStepName(String stepName) {
|
||||
this.stepName = stepName;
|
||||
}
|
||||
public Map<String, Map<String, Object>> getRequests() {
|
||||
return requests;
|
||||
}
|
||||
public void setRequests(Map<String, Map<String, Object>> requests) {
|
||||
this.requests = requests;
|
||||
}
|
||||
public Map getResult() {
|
||||
return result;
|
||||
}
|
||||
public void setResult(Map result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
}
|
||||
160
src/main/java/com/wehotel/fizz/input/ClientInputConfig.java
Normal file
160
src/main/java/com/wehotel/fizz/input/ClientInputConfig.java
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz.input;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
* @author francis
|
||||
*
|
||||
*/
|
||||
public class ClientInputConfig extends InputConfig {
|
||||
|
||||
private boolean debug;
|
||||
private String path;
|
||||
private String method;
|
||||
private Map<String, Object> headers = new HashMap<String, Object>();
|
||||
private Map<String, Object> langDef;
|
||||
private Map<String, Object> bodyDef;
|
||||
private Map<String, Object> headersDef;
|
||||
private Map<String, Object> paramsDef;
|
||||
private Map<String, Object> scriptValidate;
|
||||
private Map<String, Object> validateResponse;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ClientInputConfig(Map configBody) {
|
||||
if(configBody.get("debug") != null) {
|
||||
this.debug = (boolean) configBody.get("debug");
|
||||
}
|
||||
this.path = (String) configBody.get("path");
|
||||
if (configBody.get("headers") != null) {
|
||||
setHeaders((Map) configBody.get("headers"));
|
||||
}
|
||||
if (configBody.get("method") != null) {
|
||||
setMethod((String) configBody.get("method"));
|
||||
} else {
|
||||
setMethod("GET");
|
||||
}
|
||||
|
||||
if (configBody.get("langDef") != null) {
|
||||
langDef = ((Map) configBody.get("langDef"));
|
||||
}
|
||||
if (configBody.get("bodyDef") != null) {
|
||||
bodyDef = ((Map) configBody.get("bodyDef"));
|
||||
}
|
||||
if (configBody.get("paramsDef") != null) {
|
||||
paramsDef = ((Map) configBody.get("paramsDef"));
|
||||
}
|
||||
if (configBody.get("headersDef") != null) {
|
||||
headersDef = ((Map) configBody.get("headersDef"));
|
||||
}
|
||||
if (configBody.get("scriptValidate") != null) {
|
||||
scriptValidate = ((Map) configBody.get("scriptValidate"));
|
||||
}
|
||||
if (configBody.get("validateResponse") != null) {
|
||||
validateResponse = ((Map) configBody.get("validateResponse"));
|
||||
}
|
||||
}
|
||||
|
||||
public ClientInputConfig() {
|
||||
|
||||
}
|
||||
|
||||
public boolean isDebug() {
|
||||
return debug;
|
||||
}
|
||||
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public Map<String, Object> getLangDef() {
|
||||
return langDef;
|
||||
}
|
||||
|
||||
public void setLangDef(Map<String, Object> langDef) {
|
||||
this.langDef = langDef;
|
||||
}
|
||||
|
||||
public Map<String, Object> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public void setHeaders(Map<String, Object> headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public Map<String, Object> getBodyDef() {
|
||||
return bodyDef;
|
||||
}
|
||||
|
||||
public void setBodyDef(Map<String, Object> bodyDef) {
|
||||
this.bodyDef = bodyDef;
|
||||
}
|
||||
|
||||
public Map<String, Object> getHeadersDef() {
|
||||
return headersDef;
|
||||
}
|
||||
|
||||
public void setHeadersDef(Map<String, Object> headersDef) {
|
||||
this.headersDef = headersDef;
|
||||
}
|
||||
|
||||
public Map<String, Object> getParamsDef() {
|
||||
return paramsDef;
|
||||
}
|
||||
|
||||
public void setParamsDef(Map<String, Object> paramsDef) {
|
||||
this.paramsDef = paramsDef;
|
||||
}
|
||||
|
||||
public Map<String, Object> getScriptValidate() {
|
||||
return scriptValidate;
|
||||
}
|
||||
|
||||
public void setScriptValidate(Map<String, Object> scriptValidate) {
|
||||
this.scriptValidate = scriptValidate;
|
||||
}
|
||||
|
||||
public Map<String, Object> getValidateResponse() {
|
||||
return validateResponse;
|
||||
}
|
||||
|
||||
public void setValidateResponse(Map<String, Object> validateResponse) {
|
||||
this.validateResponse = validateResponse;
|
||||
}
|
||||
}
|
||||
83
src/main/java/com/wehotel/fizz/input/Input.java
Normal file
83
src/main/java/com/wehotel/fizz/input/Input.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.fizz.input;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.wehotel.fizz.StepContext;
|
||||
import com.wehotel.fizz.StepResponse;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
*
|
||||
*/
|
||||
public class Input {
|
||||
protected String name;
|
||||
protected InputConfig config;
|
||||
protected InputContext inputContext;
|
||||
protected StepResponse lastStepResponse = null;
|
||||
protected StepResponse stepResponse;
|
||||
|
||||
public void setConfig(InputConfig inputConfig) {
|
||||
config = inputConfig;
|
||||
}
|
||||
public InputConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void beforeRun(InputContext context) {
|
||||
this.inputContext = context;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if (name == null) {
|
||||
return name = "input" + (int)(Math.random()*100);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查该Input是否需要运行,默认都运行
|
||||
* @stepContext Step上下文
|
||||
* @return TRUE:运行
|
||||
*/
|
||||
public boolean needRun(StepContext<String, Object> stepContext) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
public Mono<Map> run() {
|
||||
return null;
|
||||
}
|
||||
public void setName(String configName) {
|
||||
this.name = configName;
|
||||
|
||||
}
|
||||
|
||||
public StepResponse getStepResponse() {
|
||||
return stepResponse;
|
||||
}
|
||||
public void setStepResponse(StepResponse stepResponse) {
|
||||
this.stepResponse = stepResponse;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
48
src/main/java/com/wehotel/fizz/input/InputConfig.java
Normal file
48
src/main/java/com/wehotel/fizz/input/InputConfig.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz.input;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
*
|
||||
*/
|
||||
public class InputConfig {
|
||||
|
||||
private InputType type;
|
||||
protected Map<String, Object> dataMapping;
|
||||
|
||||
public InputType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(InputType typeEnum) {
|
||||
this.type = typeEnum;
|
||||
}
|
||||
|
||||
public Map<String, Object> getDataMapping() {
|
||||
return dataMapping;
|
||||
}
|
||||
|
||||
public void setDataMapping(Map<String, Object> dataMapping) {
|
||||
this.dataMapping = dataMapping;
|
||||
}
|
||||
|
||||
}
|
||||
68
src/main/java/com/wehotel/fizz/input/InputContext.java
Normal file
68
src/main/java/com/wehotel/fizz/input/InputContext.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package com.wehotel.fizz.input;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.wehotel.fizz.StepContext;
|
||||
import com.wehotel.fizz.StepResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
*
|
||||
*/
|
||||
public class InputContext {
|
||||
private StepContext<String, Object> stepContext;
|
||||
private StepResponse lastStepResponse = null;
|
||||
public InputContext(StepContext<String, Object> stepContext2, StepResponse lastStepResponse2) {
|
||||
this.stepContext = stepContext2;
|
||||
this.lastStepResponse = lastStepResponse2;
|
||||
}
|
||||
public StepContext<String, Object> getStepContext() {
|
||||
return stepContext;
|
||||
}
|
||||
public void setStepContext(StepContext<String, Object> stepContext) {
|
||||
this.stepContext = stepContext;
|
||||
}
|
||||
public StepResponse getLastStepResponse() {
|
||||
return lastStepResponse;
|
||||
}
|
||||
public void setLastStepResponse(StepResponse lastStepResponse) {
|
||||
this.lastStepResponse = lastStepResponse;
|
||||
}
|
||||
// public Map<String, Object> getResponses() {
|
||||
// //TODO:
|
||||
// if (stepContext != null) {
|
||||
// Map<String, Object> responses = new HashMap<String, Object>();
|
||||
// for( String key :stepContext.keySet()) {
|
||||
// StepResponse stepResponse = (StepResponse)stepContext.get(key);
|
||||
// responses.put(key, stepResponse.getResponse());
|
||||
// }
|
||||
// return responses;
|
||||
// } else {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
||||
62
src/main/java/com/wehotel/fizz/input/InputFactory.java
Normal file
62
src/main/java/com/wehotel/fizz/input/InputFactory.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz.input;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
*
|
||||
*/
|
||||
public class InputFactory {
|
||||
public static InputConfig createInputConfig(Map config) {
|
||||
String type = (String) config.get("type");
|
||||
InputType typeEnum = InputType.valueOf(type.toUpperCase());
|
||||
InputConfig inputConfig = null;
|
||||
switch(typeEnum) {
|
||||
case REQUEST:
|
||||
inputConfig = new RequestInputConfig(config);
|
||||
|
||||
break;
|
||||
case MYSQL:
|
||||
inputConfig = new MySQLInputConfig(config);
|
||||
break;
|
||||
}
|
||||
inputConfig.setType(typeEnum);
|
||||
inputConfig.setDataMapping((Map<String, Object>) config.get("dataMapping"));
|
||||
|
||||
return inputConfig;
|
||||
}
|
||||
|
||||
public static Input createInput(String type) {
|
||||
InputType typeEnum = InputType.valueOf(type.toUpperCase());
|
||||
Input input = null;
|
||||
switch(typeEnum) {
|
||||
case REQUEST:
|
||||
input = new RequestInput();
|
||||
break;
|
||||
case MYSQL:
|
||||
input = new MySQLInput();
|
||||
break;
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
}
|
||||
32
src/main/java/com/wehotel/fizz/input/InputType.java
Normal file
32
src/main/java/com/wehotel/fizz/input/InputType.java
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz.input;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
*
|
||||
*/
|
||||
public enum InputType {
|
||||
REQUEST("REQUEST"),
|
||||
MYSQL("MYSQL");
|
||||
private final String type;
|
||||
private InputType(String aType) {
|
||||
this.type = aType;
|
||||
}
|
||||
}
|
||||
27
src/main/java/com/wehotel/fizz/input/MySQLInput.java
Normal file
27
src/main/java/com/wehotel/fizz/input/MySQLInput.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz.input;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
*
|
||||
*/
|
||||
public class MySQLInput extends Input {
|
||||
|
||||
}
|
||||
33
src/main/java/com/wehotel/fizz/input/MySQLInputConfig.java
Normal file
33
src/main/java/com/wehotel/fizz/input/MySQLInputConfig.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz.input;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
*
|
||||
*/
|
||||
public class MySQLInputConfig extends InputConfig {
|
||||
|
||||
public MySQLInputConfig(Map configBody) {
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
}
|
||||
315
src/main/java/com/wehotel/fizz/input/PathMapping.java
Normal file
315
src/main/java/com/wehotel/fizz/input/PathMapping.java
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz.input;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.noear.snack.ONode;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.wehotel.constants.CommonConstants;
|
||||
import com.wehotel.fizz.StepContext;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author francis
|
||||
*
|
||||
*/
|
||||
public class PathMapping {
|
||||
|
||||
public static ONode toONode(Object obj) {
|
||||
ONode o = null;
|
||||
synchronized (obj) {
|
||||
o = ONode.loadObj(obj);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static void setByPath(ONode target, String path, Object obj) {
|
||||
if (CommonConstants.WILDCARD_STAR.equals(path)) {
|
||||
if (obj instanceof ONode) {
|
||||
ONode node = (ONode) obj;
|
||||
if(node.isObject()) {
|
||||
target.setAll(node);
|
||||
}
|
||||
} else if (obj instanceof Map) {
|
||||
target.setAll((Map) obj);
|
||||
}
|
||||
} else {
|
||||
String[] keys = path.split("\\.");
|
||||
ONode cur = target;
|
||||
for (int i = 0; i < keys.length - 1; i++) {
|
||||
cur = cur.get(keys[i]);
|
||||
}
|
||||
|
||||
if ((obj instanceof ONode && ((ONode) obj).isArray()) || obj instanceof Collection ||
|
||||
(obj instanceof ONode && ((ONode) obj).isObject()) || obj instanceof Map) {
|
||||
if (cur == null) {
|
||||
target.get(keys[keys.length - 1]).fill(obj);
|
||||
} else {
|
||||
cur.get(keys[keys.length - 1]).fill(obj);
|
||||
}
|
||||
} else {
|
||||
if (cur == null) {
|
||||
target.set(keys[keys.length - 1], obj);
|
||||
} else {
|
||||
cur.set(keys[keys.length - 1], obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, Object> transformToMap(ONode ctxNode, Map<String, Object> rules) {
|
||||
ONode target = transform(ctxNode, rules);
|
||||
return target.toObject(Map.class);
|
||||
}
|
||||
|
||||
public static ONode transform(ONode ctxNode, Map<String, Object> rules) {
|
||||
ONode target = ONode.load(new HashMap());
|
||||
if (rules.isEmpty()) {
|
||||
return target;
|
||||
}
|
||||
|
||||
Map<String, String> rs = new HashMap<>();
|
||||
Map<String, String> types = new HashMap<>();
|
||||
for (Entry<String, Object> entry : rules.entrySet()) {
|
||||
if (entry.getValue() instanceof String) {
|
||||
String val = (String) entry.getValue();
|
||||
String[] vals = val.split(" ");
|
||||
if (vals.length > 1) {
|
||||
rs.put(entry.getKey(), vals[1]);
|
||||
types.put(entry.getKey(), vals[0]);
|
||||
} else {
|
||||
rs.put(entry.getKey(), val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rs.isEmpty()) {
|
||||
return target;
|
||||
}
|
||||
|
||||
// wildcard star entry
|
||||
Object starValObj = null;
|
||||
String starEntryKey = null;
|
||||
|
||||
for (Entry<String, String> entry : rs.entrySet()) {
|
||||
ONode val = ctxNode.select("$." + handlePath(entry.getValue()));
|
||||
|
||||
Object obj = val;
|
||||
if (val != null && types.containsKey(entry.getKey())) {
|
||||
switch (types.get(entry.getKey())) {
|
||||
case "Integer":
|
||||
case "int": {
|
||||
obj = val.val().isNull() ? null : val.val().getInt();
|
||||
break;
|
||||
}
|
||||
case "Boolean":
|
||||
case "boolean": {
|
||||
obj = val.val().isNull() ? null : val.val().getBoolean();
|
||||
break;
|
||||
}
|
||||
case "Float":
|
||||
case "float": {
|
||||
obj = val.val().isNull() ? null : val.val().getFloat();
|
||||
break;
|
||||
}
|
||||
case "Double":
|
||||
case "double": {
|
||||
obj = val.val().isNull() ? null : val.val().getDouble();
|
||||
break;
|
||||
}
|
||||
case "String": {
|
||||
obj = val.val().isNull() ? null : val.val().getString();
|
||||
break;
|
||||
}
|
||||
case "Long":
|
||||
case "long": {
|
||||
obj = val.val().isNull() ? null : val.val().getLong();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (CommonConstants.WILDCARD_STAR.equals(entry.getKey())) {
|
||||
starValObj = obj;
|
||||
starEntryKey = entry.getKey();
|
||||
}else {
|
||||
setByPath(target, entry.getKey(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
if(starEntryKey != null) {
|
||||
setByPath(target, starEntryKey, starValObj);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
public static Map<String, Object> getScriptRules(Map<String, Object> rules) {
|
||||
if (rules.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
Map<String, Object> rs = new HashMap<>();
|
||||
for (Entry<String, Object> entry : rules.entrySet()) {
|
||||
if (!(entry.getValue() instanceof String)) {
|
||||
rs.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把Path转为context里的实际路径<br/>
|
||||
* 步骤兼容以下几种写法,把后几种转换为第一种标准路径<br/>
|
||||
*
|
||||
* 例子1:<br/>
|
||||
* step1.requests.request1.request.headers<br/>
|
||||
* step1.request1.request.headers<br/>
|
||||
* step1.request1.requestHeaders<br/>
|
||||
* step1.requests.request1.requestHeaders<br/>
|
||||
*
|
||||
* 例子2:<br/>
|
||||
* step1.requests.request1.response.body<br/>
|
||||
* step1.request1.response.body<br/>
|
||||
* step1.request1.responseBody<br/>
|
||||
* step1.requests.request1.responseBody<br/>
|
||||
*
|
||||
* input兼容以下写法,把第二种转换为第一种标准路径<br/>
|
||||
*
|
||||
* 例子1:<br/>
|
||||
* input.request.headers<br/>
|
||||
* input.requestHeaders<br/>
|
||||
*
|
||||
* 例子2:<br/>
|
||||
* input.response.body<br/>
|
||||
* input.responseBody<br/>
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public static String handlePath(String path) {
|
||||
if(path.startsWith("step")) {
|
||||
String[] arr = path.split("\\.");
|
||||
|
||||
List<String> list = Arrays.stream(arr).collect(Collectors.toList());
|
||||
// 补齐 requests
|
||||
if(list.size() >= 2 && !"requests".equals(list.get(1))) {
|
||||
list.add(1,"requests");
|
||||
}
|
||||
|
||||
// 拆分一级为两级,如:requestBody -> request.body
|
||||
if(list.size() >= 4) {
|
||||
String s = list.get(3);
|
||||
switch (s) {
|
||||
case "requestHeaders":
|
||||
list.set(3, "headers");
|
||||
list.add(3, "request");
|
||||
break;
|
||||
case "requestParams":
|
||||
list.set(3, "params");
|
||||
list.add(3, "request");
|
||||
break;
|
||||
case "requestBody":
|
||||
list.set(3, "body");
|
||||
list.add(3, "request");
|
||||
break;
|
||||
case "responseHeaders":
|
||||
list.set(3, "headers");
|
||||
list.add(3, "response");
|
||||
break;
|
||||
case "responseBody":
|
||||
list.set(3, "body");
|
||||
list.add(3, "response");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return String.join(".", list);
|
||||
}else if(path.startsWith("input")) {
|
||||
String[] arr = path.split("\\.");
|
||||
|
||||
List<String> list = Arrays.stream(arr).collect(Collectors.toList());
|
||||
|
||||
// 拆分一级为两级,如:requestBody -> request.body
|
||||
if(list.size() >= 2) {
|
||||
String s = list.get(1);
|
||||
switch (s) {
|
||||
case "requestHeaders":
|
||||
list.set(1, "headers");
|
||||
list.add(1, "request");
|
||||
break;
|
||||
case "requestParams":
|
||||
list.set(1, "params");
|
||||
list.add(1, "request");
|
||||
break;
|
||||
case "requestBody":
|
||||
list.set(1, "body");
|
||||
list.add(1, "request");
|
||||
break;
|
||||
case "responseHeaders":
|
||||
list.set(1, "headers");
|
||||
list.add(1, "response");
|
||||
break;
|
||||
case "responseBody":
|
||||
list.set(1, "body");
|
||||
list.add(1, "response");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return String.join(".", list);
|
||||
|
||||
}else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据转换
|
||||
*
|
||||
* @param ctxNode
|
||||
* @param stepContext
|
||||
* @param fixed optional
|
||||
* @param mappingRules optional
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, Object> transform(ONode ctxNode, StepContext<String, Object> stepContext,
|
||||
Map<String, Object> fixed, Map<String, Object> mappingRules) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
if (fixed != null) {
|
||||
result.putAll((Map<String, Object>) fixed);
|
||||
}
|
||||
if (mappingRules != null) {
|
||||
// 路径映射
|
||||
ONode target = PathMapping.transform(ctxNode, mappingRules);
|
||||
// 脚本转换
|
||||
Map<String, Object> scriptRules = PathMapping.getScriptRules(mappingRules);
|
||||
Map<String, Object> scriptResult = ScriptHelper.executeScripts(target, scriptRules, ctxNode, stepContext);
|
||||
if (scriptResult != null && !scriptResult.isEmpty()) {
|
||||
result.putAll(scriptResult);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
334
src/main/java/com/wehotel/fizz/input/RequestInput.java
Normal file
334
src/main/java/com/wehotel/fizz/input/RequestInput.java
Normal file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz.input;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import org.noear.snack.ONode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.wehotel.flume.clients.log4j2appender.LogService;
|
||||
import com.wehotel.FizzGatewayApplication;
|
||||
import com.wehotel.constants.CommonConstants;
|
||||
import com.wehotel.fizz.StepContext;
|
||||
import com.wehotel.fizz.StepResponse;
|
||||
import com.wehotel.proxy.FizzWebClient;
|
||||
import com.wehotel.util.MapUtil;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
* @author francis
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class RequestInput extends Input {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RequestInput.class);
|
||||
private InputType type;
|
||||
protected Map<String, Object> dataMapping;
|
||||
protected Map<String, Object> request = new HashMap<>();
|
||||
protected Map<String, Object> response = new HashMap<>();
|
||||
|
||||
private static final String FALLBACK_MODE_STOP = "stop";
|
||||
private static final String FALLBACK_MODE_CONTINUE = "continue";
|
||||
|
||||
public InputType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(InputType typeEnum) {
|
||||
this.type = typeEnum;
|
||||
}
|
||||
|
||||
public Map<String, Object> getDataMapping() {
|
||||
return dataMapping;
|
||||
}
|
||||
|
||||
public void setDataMapping(Map<String, Object> dataMapping) {
|
||||
this.dataMapping = dataMapping;
|
||||
}
|
||||
|
||||
private void doRequestMapping(InputConfig aConfig, InputContext inputContext) {
|
||||
RequestInputConfig config = (RequestInputConfig) aConfig;
|
||||
|
||||
// 把请求信息放入stepContext
|
||||
Map<String, Object> group = new HashMap<>();
|
||||
group.put("request", request);
|
||||
group.put("response", response);
|
||||
this.stepResponse.getRequests().put(name, group);
|
||||
|
||||
HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase());
|
||||
request.put("method", method);
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.putAll(MapUtil.toHashMap(config.getQueryParams()));
|
||||
request.put("params", params);
|
||||
|
||||
// 数据转换
|
||||
if (inputContext != null && inputContext.getStepContext() != null) {
|
||||
StepContext<String, Object> stepContext = inputContext.getStepContext();
|
||||
Map<String, Object> dataMapping = this.getConfig().getDataMapping();
|
||||
if (dataMapping != null) {
|
||||
Map<String, Object> requestMapping = (Map<String, Object>) dataMapping.get("request");
|
||||
if (requestMapping != null && !StringUtils.isEmpty(requestMapping)) {
|
||||
ONode ctxNode = PathMapping.toONode(stepContext);
|
||||
|
||||
// headers
|
||||
request.put("headers",
|
||||
PathMapping.transform(ctxNode, stepContext,
|
||||
(Map<String, Object>) requestMapping.get("fixedHeaders"),
|
||||
(Map<String, Object>) requestMapping.get("headers")));
|
||||
|
||||
// params
|
||||
params.putAll(PathMapping.transform(ctxNode, stepContext,
|
||||
(Map<String, Object>) requestMapping.get("fixedParams"),
|
||||
(Map<String, Object>) requestMapping.get("params")));
|
||||
request.put("params", params);
|
||||
|
||||
// body
|
||||
Map<String,Object> body = PathMapping.transform(ctxNode, stepContext,
|
||||
(Map<String, Object>) requestMapping.get("fixedBody"),
|
||||
(Map<String, Object>) requestMapping.get("body"));
|
||||
|
||||
|
||||
// script
|
||||
if (requestMapping.get("script") != null) {
|
||||
Map<String, Object> scriptCfg = (Map<String, Object>) requestMapping.get("script");
|
||||
try {
|
||||
Object reqBody = ScriptHelper.execute(scriptCfg, ctxNode, stepContext);
|
||||
if (reqBody != null) {
|
||||
body.putAll((Map<String, Object>) reqBody);
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
LogService.setBizId(inputContext.getStepContext().getTraceId());
|
||||
LOGGER.warn("execute script failed, {}", e);
|
||||
throw new RuntimeException("execute script failed");
|
||||
}
|
||||
}
|
||||
request.put("body", body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(config.getBaseUrl() + config.getPath())
|
||||
.queryParams(MapUtil.toMultiValueMap(params)).build();
|
||||
request.put("url", uriComponents.toUriString());
|
||||
}
|
||||
|
||||
private void doResponseMapping(InputConfig aConfig, InputContext inputContext, String responseBody) {
|
||||
RequestInputConfig config = (RequestInputConfig) aConfig;
|
||||
response.put("body", JSON.parse(responseBody));
|
||||
// 数据转换
|
||||
if (inputContext != null && inputContext.getStepContext() != null) {
|
||||
StepContext<String, Object> stepContext = inputContext.getStepContext();
|
||||
Map<String, Object> dataMapping = this.getConfig().getDataMapping();
|
||||
if (dataMapping != null) {
|
||||
Map<String, Object> responseMapping = (Map<String, Object>) dataMapping.get("response");
|
||||
if (responseMapping != null && !StringUtils.isEmpty(responseMapping)) {
|
||||
ONode ctxNode = PathMapping.toONode(stepContext);
|
||||
|
||||
// headers
|
||||
Map<String, Object> fixedHeaders = (Map<String, Object>) responseMapping.get("fixedHeaders");
|
||||
Map<String, Object> headerMapping = (Map<String, Object>) responseMapping.get("headers");
|
||||
if ((fixedHeaders != null && !fixedHeaders.isEmpty())
|
||||
|| (headerMapping != null && !headerMapping.isEmpty())) {
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
headers.putAll(PathMapping.transform(ctxNode, stepContext, fixedHeaders, headerMapping));
|
||||
response.put("headers", headers);
|
||||
}
|
||||
|
||||
// body
|
||||
Map<String, Object> fixedBody = (Map<String, Object>) responseMapping.get("fixedBody");
|
||||
Map<String, Object> bodyMapping = (Map<String, Object>) responseMapping.get("body");
|
||||
Map<String, Object> scriptCfg = (Map<String, Object>) responseMapping.get("script");
|
||||
if ((fixedBody != null && !fixedBody.isEmpty()) || (bodyMapping != null && !bodyMapping.isEmpty())
|
||||
|| (scriptCfg != null && scriptCfg.get("type") != null
|
||||
&& scriptCfg.get("source") != null)) {
|
||||
// body
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.putAll(PathMapping.transform(ctxNode, stepContext, fixedBody, bodyMapping));
|
||||
|
||||
// script
|
||||
if (scriptCfg != null && scriptCfg.get("type") != null && scriptCfg.get("source") != null) {
|
||||
try {
|
||||
Object respBody = ScriptHelper.execute(scriptCfg, ctxNode, stepContext);
|
||||
if (respBody != null) {
|
||||
body.putAll((Map<String, Object>) respBody);
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
LogService.setBizId(inputContext.getStepContext().getTraceId());
|
||||
LOGGER.warn("execute script failed, {}", e);
|
||||
throw new RuntimeException("execute script failed");
|
||||
}
|
||||
}
|
||||
response.put("body", body);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
response.put("body", JSON.parse(responseBody));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Mono<ClientResponse> getClientSpecFromContext(InputConfig aConfig, InputContext inputContext) {
|
||||
RequestInputConfig config = (RequestInputConfig) aConfig;
|
||||
|
||||
int timeout = config.getTimeout() < 1 ? 3000 : config.getTimeout() > 10000 ? 10000 : config.getTimeout();
|
||||
|
||||
HttpMethod method = HttpMethod.valueOf(config.getMethod());
|
||||
String url = (String) request.get("url");
|
||||
|
||||
Map<String, Object> headers = (Map<String, Object>) request.get("headers");
|
||||
if (headers == null) {
|
||||
headers = new HashMap<>();
|
||||
}
|
||||
if (!headers.containsKey("Content-Type")) {
|
||||
// defalut content-type
|
||||
headers.put("Content-Type", "application/json; charset=UTF-8");
|
||||
}
|
||||
headers.put(CommonConstants.HEADER_TRACE_ID, inputContext.getStepContext().getTraceId());
|
||||
|
||||
HttpMethod aggrMethod = HttpMethod.valueOf(inputContext.getStepContext().getInputReqAttr("method").toString());
|
||||
String aggrPath = (String)inputContext.getStepContext().getInputReqAttr("path");
|
||||
String aggrService = aggrPath.split("\\/")[2];
|
||||
|
||||
FizzWebClient client = FizzGatewayApplication.appContext.getBean(FizzWebClient.class);
|
||||
return client.aggrSend(aggrService, aggrMethod, aggrPath, null, method, url,
|
||||
MapUtil.toHttpHeaders(headers), request.get("body"), (long)timeout);
|
||||
}
|
||||
|
||||
private Map<String, Object> getResponses(Map<String, StepResponse> stepContext2) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean needRun(StepContext<String, Object> stepContext) {
|
||||
Map<String, Object> condition = ((RequestInputConfig) config).getCondition();
|
||||
if (CollectionUtils.isEmpty(condition)) {
|
||||
// 没有配置condition,直接运行
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
ONode ctxNode = PathMapping.toONode(stepContext);
|
||||
try {
|
||||
Boolean needRun = ScriptHelper.execute(condition, ctxNode, stepContext, Boolean.class);
|
||||
return needRun != null ? needRun : Boolean.TRUE;
|
||||
} catch (ScriptException e) {
|
||||
LogService.setBizId(inputContext.getStepContext().getTraceId());
|
||||
LOGGER.warn("execute script failed", e);
|
||||
throw new RuntimeException("execute script failed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Map> run() {
|
||||
long t1 = System.currentTimeMillis();
|
||||
this.doRequestMapping(config, inputContext);
|
||||
inputContext.getStepContext().addElapsedTime(stepResponse.getStepName() + "-" + this.name + "-RequestMapping",
|
||||
System.currentTimeMillis() - t1);
|
||||
|
||||
String prefix = stepResponse.getStepName() + "-" + "调用接口";
|
||||
long start = System.currentTimeMillis();
|
||||
Mono<ClientResponse> clientResponse = this.getClientSpecFromContext(config, inputContext);
|
||||
Mono<String> body = clientResponse.flatMap(cr->{
|
||||
return Mono.just(cr).doOnError(throwable -> cleanup(cr));
|
||||
}).doOnSuccess(cr -> {
|
||||
long elapsedMillis = System.currentTimeMillis() - start;
|
||||
HttpHeaders httpHeaders = cr.headers().asHttpHeaders();
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
httpHeaders.forEach((key, value) -> {
|
||||
if (value.size() > 1) {
|
||||
headers.put(key, value);
|
||||
} else {
|
||||
headers.put(key, httpHeaders.getFirst(key));
|
||||
}
|
||||
});
|
||||
headers.put("elapsedTime", elapsedMillis + "ms");
|
||||
this.response.put("headers", headers);
|
||||
inputContext.getStepContext().addElapsedTime(prefix + request.get("url"),
|
||||
elapsedMillis);
|
||||
}).flatMap(cr -> cr.bodyToMono(String.class)).doOnSuccess(resp -> {
|
||||
long elapsedMillis = System.currentTimeMillis() - start;
|
||||
if(inputContext.getStepContext().isDebug()) {
|
||||
LogService.setBizId(inputContext.getStepContext().getTraceId());
|
||||
LOGGER.info("{} 耗时:{}ms URL={}, reqHeader={} req={} resp={}", prefix, elapsedMillis, request.get("url"),
|
||||
JSON.toJSONString(this.request.get("headers")),
|
||||
JSON.toJSONString(this.request.get("body")), resp);
|
||||
}
|
||||
}).doOnError(ex -> {
|
||||
LogService.setBizId(inputContext.getStepContext().getTraceId());
|
||||
LOGGER.warn("failed to call {}", request.get("url"), ex);
|
||||
long elapsedMillis = System.currentTimeMillis() - start;
|
||||
inputContext.getStepContext().addElapsedTime(
|
||||
stepResponse.getStepName() + "-" + "调用接口 failed " + request.get("url"), elapsedMillis);
|
||||
});
|
||||
|
||||
// fallback handler
|
||||
RequestInputConfig reqConfig = (RequestInputConfig) config;
|
||||
if (reqConfig.getFallback() != null) {
|
||||
Map<String, String> fallback = reqConfig.getFallback();
|
||||
String mode = fallback.get("mode");
|
||||
if (FALLBACK_MODE_STOP.equals(mode)) {
|
||||
body = body.onErrorStop();
|
||||
} else if (FALLBACK_MODE_CONTINUE.equals(mode)) {
|
||||
body = body.onErrorResume(ex -> {
|
||||
return Mono.just(fallback.get("defaultResult"));
|
||||
});
|
||||
} else {
|
||||
body = body.onErrorStop();
|
||||
}
|
||||
}
|
||||
|
||||
return body.flatMap(item -> {
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
result.put("data", item);
|
||||
result.put("request", this);
|
||||
|
||||
long t3 = System.currentTimeMillis();
|
||||
this.doResponseMapping(config, inputContext, item);
|
||||
inputContext.getStepContext().addElapsedTime(
|
||||
stepResponse.getStepName() + "-" + this.name + "-ResponseMapping", System.currentTimeMillis() - t3);
|
||||
|
||||
return Mono.just(result);
|
||||
});
|
||||
}
|
||||
|
||||
private void cleanup(ClientResponse clientResponse) {
|
||||
if (clientResponse != null) {
|
||||
clientResponse.bodyToMono(Void.class).subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
129
src/main/java/com/wehotel/fizz/input/RequestInputConfig.java
Normal file
129
src/main/java/com/wehotel/fizz/input/RequestInputConfig.java
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz.input;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author linwaiwai
|
||||
* @author francis
|
||||
*
|
||||
*/
|
||||
public class RequestInputConfig extends InputConfig{
|
||||
private URL url ;
|
||||
private String method ;
|
||||
private int timeout = 3;
|
||||
private Map<String,String> fallback = new HashMap<String, String>();
|
||||
private Map<String, Object> condition;
|
||||
|
||||
public RequestInputConfig(Map configBody) {
|
||||
String url = (String) configBody.get("url");
|
||||
if(StringUtils.isEmpty(url)) {
|
||||
throw new RuntimeException("Request URL can not be blank");
|
||||
}
|
||||
setUrl(url);
|
||||
if (configBody.get("method") != null) {
|
||||
setMethod(((String)configBody.get("method")).toUpperCase());
|
||||
} else {
|
||||
setMethod("GET");
|
||||
}
|
||||
if (configBody.get("timeout") != null) {
|
||||
timeout = Integer.valueOf(configBody.get("timeout").toString());
|
||||
}
|
||||
if (configBody.get("fallback") != null) {
|
||||
fallback = (Map<String,String>)configBody.get("fallback");
|
||||
}
|
||||
if (configBody.get("condition") != null) {
|
||||
setCondition((Map)configBody.get("condition"));
|
||||
}
|
||||
}
|
||||
|
||||
public String getQueryStr(){
|
||||
return url.getQuery();
|
||||
}
|
||||
|
||||
public MultiValueMap<String, String> getQueryParams(){
|
||||
MultiValueMap<String, String> parameters =
|
||||
UriComponentsBuilder.fromUriString(url.toString()).build().getQueryParams();
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
||||
public String getBaseUrl() {
|
||||
return url.getProtocol()+ "://"+ url.getHost() + (url.getPort() == -1 ? "" : ":" + url.getPort());
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return url.getPath();
|
||||
}
|
||||
|
||||
public void setUrl(String string) {
|
||||
try {
|
||||
url = new URL(string);
|
||||
} catch (MalformedURLException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(int timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public Map<String, String> getFallback() {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
public void setFallback(Map<String, String> fallback) {
|
||||
this.fallback = fallback;
|
||||
}
|
||||
|
||||
public Map<String, Object> getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public void setCondition(Map<String, Object> condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
}
|
||||
151
src/main/java/com/wehotel/fizz/input/ScriptHelper.java
Normal file
151
src/main/java/com/wehotel/fizz/input/ScriptHelper.java
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.fizz.input;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import org.noear.snack.ONode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.ctrip.framework.apollo.core.utils.StringUtils;
|
||||
import com.wehotel.constants.CommonConstants;
|
||||
import com.wehotel.exception.StopAndResponseException;
|
||||
import com.wehotel.fizz.StepContext;
|
||||
import com.wehotel.util.Script;
|
||||
import com.wehotel.util.ScriptUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author francis
|
||||
*
|
||||
*/
|
||||
public class ScriptHelper {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ScriptHelper.class);
|
||||
|
||||
public static Object execute(Map<String, Object> scriptCfg, ONode ctxNode, StepContext<String, Object> stepContext)
|
||||
throws ScriptException {
|
||||
return execute(scriptCfg, ctxNode, stepContext, Object.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T execute(Map<String, Object> scriptCfg, ONode ctxNode, StepContext<String, Object> stepContext, Class<T> clazz)
|
||||
throws ScriptException {
|
||||
Script script = new Script();
|
||||
script.setType((String) scriptCfg.get("type"));
|
||||
script.setSource((String) scriptCfg.get("source"));
|
||||
if (StringUtils.isBlank(script.getType()) || StringUtils.isBlank(script.getSource())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, Object> ctx = new HashMap<>();
|
||||
ctx.put("context", stepContext);
|
||||
|
||||
Object rs = ScriptUtils.execute(script, ctx);
|
||||
if (ScriptUtils.GROOVY.equals(script.getType())) {
|
||||
return (T) handleStopResponse(stepContext, rs);
|
||||
} else if (ScriptUtils.JAVA_SCRIPT.equals(script.getType())) {
|
||||
if(rs != null) {
|
||||
if(rs instanceof Collection || rs instanceof Map) {
|
||||
return (T) rs;
|
||||
}else {
|
||||
String json = rs.toString();
|
||||
if(json.startsWith("[") && json.endsWith("]")) {
|
||||
return JSON.parseArray(json).toJavaObject(clazz);
|
||||
}else if(json.startsWith("{") && json.endsWith("}")) {
|
||||
if(clazz.isAssignableFrom(Map.class)) {
|
||||
return (T)handleStopResponse(stepContext, JSON.parseObject(json).toJavaObject(clazz));
|
||||
}else {
|
||||
handleStopResponse(stepContext, JSON.parseObject(json).toJavaObject(Map.class));
|
||||
return JSON.parseObject(json).toJavaObject(clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (T) rs;
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return (T) rs;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, Object> executeScripts(ONode target, Map<String, Object> scriptRules, ONode ctxNode,
|
||||
StepContext<String, Object> stepContext) {
|
||||
return executeScripts(target, scriptRules, ctxNode, stepContext, Object.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Map<String, T> executeScripts(ONode target, Map<String, Object> scriptRules, ONode ctxNode,
|
||||
StepContext<String, Object> stepContext, Class<T> clazz) {
|
||||
if(target == null) {
|
||||
target = ONode.load(new HashMap());
|
||||
}
|
||||
if (scriptRules != null && !scriptRules.isEmpty()) {
|
||||
// wildcard star entry
|
||||
Object starValObj = null;
|
||||
String starEntryKey = null;
|
||||
for (Entry<String, Object> entry : scriptRules.entrySet()) {
|
||||
Map<String, Object> scriptCfg = (Map<String, Object>) entry.getValue();
|
||||
try {
|
||||
if (CommonConstants.WILDCARD_STAR.equals(entry.getKey())) {
|
||||
starValObj = execute(scriptCfg, ctxNode, stepContext, clazz);
|
||||
starEntryKey = entry.getKey();
|
||||
}else {
|
||||
PathMapping.setByPath(target, entry.getKey(), execute(scriptCfg, ctxNode, stepContext, clazz));
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
LOGGER.warn("execute script failed, {}", e);
|
||||
throw new RuntimeException("execute script failed");
|
||||
}
|
||||
}
|
||||
if(starEntryKey != null) {
|
||||
PathMapping.setByPath(target, starEntryKey, starValObj);
|
||||
}
|
||||
}
|
||||
return target.toObject(Map.class);
|
||||
}
|
||||
|
||||
public static Object handleStopResponse(StepContext<String, Object> stepContext, Object result) {
|
||||
if(result instanceof Map) {
|
||||
Map<String, Object> rs = (Map<String, Object>) result;
|
||||
if (rs.containsKey(CommonConstants.STOP_AND_RESPONSE_KEY)) {
|
||||
if (rs.get(CommonConstants.STOP_AND_RESPONSE_KEY) != null
|
||||
&& rs.get(CommonConstants.STOP_AND_RESPONSE_KEY) instanceof Boolean
|
||||
&& (Boolean) rs.get(CommonConstants.STOP_AND_RESPONSE_KEY)) {
|
||||
rs.remove(CommonConstants.STOP_AND_RESPONSE_KEY);
|
||||
// 测试模式返回StepContext
|
||||
if (stepContext.returnContext()) {
|
||||
rs.put("_context", stepContext);
|
||||
}
|
||||
throw new StopAndResponseException("stop and response", JSON.toJSONString(rs));
|
||||
} else {
|
||||
rs.remove(CommonConstants.STOP_AND_RESPONSE_KEY);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.flume.clients.log4j2appender;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
public enum LogService {
|
||||
|
||||
BIZ_ID, HANDLE_STGY;
|
||||
|
||||
public static void setBizId(Object bizId) {
|
||||
}
|
||||
|
||||
public static String toKF(String topic) {
|
||||
return topic;
|
||||
}
|
||||
}
|
||||
61
src/main/java/com/wehotel/legacy/RespEntity.java
Normal file
61
src/main/java/com/wehotel/legacy/RespEntity.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.legacy;
|
||||
|
||||
import com.wehotel.util.Constants;
|
||||
import com.wehotel.util.ThreadContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
public class RespEntity {
|
||||
|
||||
private static final String f0 = "{\"msgCode\":";
|
||||
private static final String f1 = ",\"message\":\"";
|
||||
private static final String f2 = "\"}";
|
||||
|
||||
public int msgCode;
|
||||
|
||||
public String message;
|
||||
|
||||
public String reqId;
|
||||
|
||||
public RespEntity(int code, String msg, @Nullable String reqId) {
|
||||
msgCode = code;
|
||||
message = msg;
|
||||
this.reqId = reqId;
|
||||
}
|
||||
|
||||
private static final String resb = "$resb";
|
||||
|
||||
static {
|
||||
StringBuilder b = new StringBuilder(128);
|
||||
ThreadContext.set(resb, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder b = ThreadContext.getStringBuilder(resb);
|
||||
return b.append(f0).append(msgCode).append(f1).append(reqId).append(Constants.Symbol.SPACE).append(message).append(f2).toString();
|
||||
}
|
||||
|
||||
public static String toJson(int code, String msg, @Nullable String reqId) {
|
||||
return new RespEntity(code, msg, reqId).toString();
|
||||
}
|
||||
}
|
||||
209
src/main/java/com/wehotel/listener/AggregateChannelListener.java
Normal file
209
src/main/java/com/wehotel/listener/AggregateChannelListener.java
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.listener;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.wehotel.fizz.ConfigLoader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.data.redis.connection.ReactiveSubscription;
|
||||
import org.springframework.data.redis.listener.ChannelTopic;
|
||||
import org.springframework.data.redis.listener.ReactiveRedisMessageListenerContainer;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.Disposable;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
|
||||
import static com.wehotel.listener.AggregateRedisConfig.AGGREGATE_REACTIVE_REDIS_MESSAGE_LISTENER_CONTAINER;
|
||||
|
||||
/**
|
||||
* 聚合Channel监听器
|
||||
* @author zhongjie
|
||||
*/
|
||||
@Component
|
||||
public class AggregateChannelListener {
|
||||
public AggregateChannelListener(@Qualifier(AGGREGATE_REACTIVE_REDIS_MESSAGE_LISTENER_CONTAINER)
|
||||
ReactiveRedisMessageListenerContainer reactiveRedisMessageListenerContainer,
|
||||
ConfigLoader configLoader) {
|
||||
this.reactiveRedisMessageListenerContainer = reactiveRedisMessageListenerContainer;
|
||||
this.configLoader = configLoader;
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AggregateChannelListener.class);
|
||||
/**
|
||||
* 聚合配置Channel名称,fizz-manager会往该Channel发送聚合变更信息
|
||||
*/
|
||||
private static final String AGGREGATE_CHANNEL = "fizz_aggregate_channel";
|
||||
|
||||
private ReactiveRedisMessageListenerContainer reactiveRedisMessageListenerContainer;
|
||||
private ConfigLoader configLoader;
|
||||
private Disposable disposable;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
Flux<ReactiveSubscription.Message<String, String>> aggregateMessageFlux =
|
||||
reactiveRedisMessageListenerContainer.receive(ChannelTopic.of(AGGREGATE_CHANNEL));
|
||||
disposable = aggregateMessageFlux.parallel().runOn(Schedulers.parallel()).subscribe(message -> {
|
||||
String messageBody = message.getMessage();
|
||||
LOGGER.info("获取到[{}]消息[{}]", AGGREGATE_CHANNEL, messageBody);
|
||||
try {
|
||||
this.handleAggregateMessage(messageBody);
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn(String.format("处理聚合推送数据异常[%s]", message), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
if (disposable != null && !disposable.isDisposed()) {
|
||||
disposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TYPE_PUBLISH = "publish ";
|
||||
private static final String TYPE_ROLLBACK = "rollback ";
|
||||
private static final String TYPE_TEST = "test ";
|
||||
private static final String TYPE_DELETE = "delete ";
|
||||
private static final String TYPE_REFRESH = "refresh ";
|
||||
private void handleAggregateMessage(String message) throws Exception {
|
||||
if (!StringUtils.hasText(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String type;
|
||||
String data;
|
||||
if (message.startsWith(TYPE_PUBLISH)) {
|
||||
type = TYPE_PUBLISH;
|
||||
data = message.replace(TYPE_PUBLISH, "");
|
||||
} else if (message.startsWith(TYPE_ROLLBACK)) {
|
||||
type = TYPE_ROLLBACK;
|
||||
data = message.replace(TYPE_ROLLBACK, "");
|
||||
} else if (message.startsWith(TYPE_TEST)) {
|
||||
type = TYPE_TEST;
|
||||
data = message.replace(TYPE_TEST, "");
|
||||
} else if (message.startsWith(TYPE_DELETE)) {
|
||||
type = TYPE_DELETE;
|
||||
data = message.replace(TYPE_DELETE, "");
|
||||
} else if (message.startsWith(TYPE_REFRESH)) {
|
||||
type = TYPE_REFRESH;
|
||||
// 需要刷新集合配置的节点IP列表
|
||||
data = message.replace(TYPE_REFRESH, "");
|
||||
} else {
|
||||
LOGGER.warn(String.format("未知的聚合推送数据[%s]", message));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case TYPE_PUBLISH:
|
||||
case TYPE_ROLLBACK:
|
||||
case TYPE_TEST:
|
||||
configLoader.addConfig(data);
|
||||
break;
|
||||
case TYPE_DELETE:
|
||||
configLoader.deleteConfig(data);
|
||||
break;
|
||||
case TYPE_REFRESH:
|
||||
this.refreshConfig(data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static final TypeReference<List<String>> STRING_LIST_TYPE_REFERENCE = new TypeReference<List<String>>() {};
|
||||
private void refreshConfig(String allowIps) throws Exception {
|
||||
if (!this.checkIp(LOCAL_IP, allowIps)) {
|
||||
// 本机IP不在刷新列表中,直接返回
|
||||
LOGGER.info("本机IP地址[{}]不在刷新IP列表[{}]中", LOCAL_IP, allowIps);
|
||||
return;
|
||||
}
|
||||
|
||||
// 刷新配置
|
||||
configLoader.init();
|
||||
}
|
||||
|
||||
private boolean checkIp(String clientIp, String allowIps) {
|
||||
if (!StringUtils.hasText(allowIps)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
List<String> allowIpList = JSON.parseObject(allowIps, STRING_LIST_TYPE_REFERENCE);
|
||||
for (String allowIp : allowIpList) {
|
||||
boolean allow = "*".equals(allowIp) || allowIp.equals(clientIp)
|
||||
|| allowIp.contains("-") && ipIsValid(allowIp, clientIp);
|
||||
if (allow) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final String REGX_IP = "((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";
|
||||
private static final String REGX_IPB = REGX_IP + "\\-" + REGX_IP;
|
||||
|
||||
private static boolean ipIsValid(String allowIp, String clientIp) {
|
||||
if (allowIp == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (clientIp == null) {
|
||||
return false;
|
||||
}
|
||||
allowIp = allowIp.trim();
|
||||
clientIp = clientIp.trim();
|
||||
if (!allowIp.matches(REGX_IPB) || !clientIp.matches(REGX_IP)) {
|
||||
return false;
|
||||
}
|
||||
int idx = allowIp.indexOf('-');
|
||||
String[] sips = allowIp.substring(0, idx).split("\\.");
|
||||
String[] sipe = allowIp.substring(idx + 1).split("\\.");
|
||||
String[] sipt = clientIp.split("\\.");
|
||||
long ips = 0L, ipe = 0L, ipt = 0L;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
ips = ips << 8 | Integer.parseInt(sips[i]);
|
||||
ipe = ipe << 8 | Integer.parseInt(sipe[i]);
|
||||
ipt = ipt << 8 | Integer.parseInt(sipt[i]);
|
||||
}
|
||||
if (ips > ipe) {
|
||||
long t = ips;
|
||||
ips = ipe;
|
||||
ipe = t;
|
||||
}
|
||||
return ips <= ipt && ipt <= ipe;
|
||||
}
|
||||
|
||||
private static final String LOCAL_IP = AggregateChannelListener.getLocalIp();
|
||||
private static String getLocalIp() {
|
||||
try {
|
||||
InetAddress inetAddress = InetAddress.getLocalHost();
|
||||
return inetAddress.getHostAddress();
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("获取本地IP地址异常", e);
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/main/java/com/wehotel/listener/AggregateRedisConfig.java
Normal file
68
src/main/java/com/wehotel/listener/AggregateRedisConfig.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.listener;
|
||||
|
||||
import com.wehotel.config.RedisReactiveConfig;
|
||||
import com.wehotel.config.RedisReactiveProperties;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
|
||||
import org.springframework.data.redis.listener.ReactiveRedisMessageListenerContainer;
|
||||
|
||||
/**
|
||||
* 聚合配置Redis配置
|
||||
* @author zhongjie
|
||||
*/
|
||||
@Configuration
|
||||
public class AggregateRedisConfig extends RedisReactiveConfig {
|
||||
static final String AGGREGATE_REACTIVE_REDIS_PROPERTIES = "aggregateReactiveRedisProperties";
|
||||
private static final String AGGREGATE_REACTIVE_REDIS_CONNECTION_FACTORY = "aggregateReactiveRedisConnectionFactory";
|
||||
public static final String AGGREGATE_REACTIVE_REDIS_TEMPLATE = "aggregateReactiveRedisTemplate";
|
||||
static final String AGGREGATE_REACTIVE_REDIS_MESSAGE_LISTENER_CONTAINER = "aggregateReactiveRedisMessageListenerContainer";
|
||||
|
||||
@ConfigurationProperties(prefix = "aggregate.redis")
|
||||
@Configuration(AGGREGATE_REACTIVE_REDIS_PROPERTIES)
|
||||
public static class AggregateRedisReactiveProperties extends RedisReactiveProperties {
|
||||
}
|
||||
|
||||
public AggregateRedisConfig(@Qualifier(AGGREGATE_REACTIVE_REDIS_PROPERTIES) RedisReactiveProperties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean(AGGREGATE_REACTIVE_REDIS_CONNECTION_FACTORY)
|
||||
public ReactiveRedisConnectionFactory lettuceConnectionFactory() {
|
||||
return super.lettuceConnectionFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean(AGGREGATE_REACTIVE_REDIS_TEMPLATE)
|
||||
public ReactiveStringRedisTemplate reactiveStringRedisTemplate(
|
||||
@Qualifier(AGGREGATE_REACTIVE_REDIS_CONNECTION_FACTORY) ReactiveRedisConnectionFactory factory) {
|
||||
return super.reactiveStringRedisTemplate(factory);
|
||||
}
|
||||
|
||||
@Bean(AGGREGATE_REACTIVE_REDIS_MESSAGE_LISTENER_CONTAINER)
|
||||
public ReactiveRedisMessageListenerContainer aggregateReactiveRedisMessageListenerContainer(
|
||||
@Qualifier(AGGREGATE_REACTIVE_REDIS_CONNECTION_FACTORY) ReactiveRedisConnectionFactory factory) {
|
||||
return new ReactiveRedisMessageListenerContainer(factory);
|
||||
}
|
||||
}
|
||||
100
src/main/java/com/wehotel/plugin/FixedPluginFilter.java
Normal file
100
src/main/java/com/wehotel/plugin/FixedPluginFilter.java
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.plugin;
|
||||
|
||||
import com.wehotel.FizzGatewayApplication;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public abstract class FixedPluginFilter extends PluginFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(FixedPluginFilter.class);
|
||||
|
||||
private static Map<String, FixedPluginFilter> fixedPluginFilterMap = new HashMap<>();
|
||||
|
||||
private static List<FixedPluginFilter> fixedPluginFilterList;
|
||||
|
||||
private static void filters2sb(StringBuilder b) {
|
||||
int sz = fixedPluginFilterList.size() - 1;
|
||||
for (int i = 0; i < fixedPluginFilterList.size(); i++) {
|
||||
FixedPluginFilter f = fixedPluginFilterList.get(i);
|
||||
b.append(f.getId()).append('=').append(f);
|
||||
if (i != sz) {
|
||||
b.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void add(FixedPluginFilter pf) {
|
||||
List<FixedPluginFilter> lst = new ArrayList<>();
|
||||
lst.addAll(fixedPluginFilterList);
|
||||
lst.add(pf);
|
||||
Collections.sort(lst,
|
||||
(fa, fb) -> {
|
||||
return fa.getOrder() - fb.getOrder();
|
||||
}
|
||||
);
|
||||
String fid = pf.getId();
|
||||
fixedPluginFilterMap.put(fid, pf);
|
||||
StringBuilder b = new StringBuilder(256);
|
||||
b.append("add ").append(fid).append('\n');
|
||||
b.append("fixed plugin filters: \n");
|
||||
filters2sb(b);
|
||||
log.info(b.toString());
|
||||
}
|
||||
|
||||
public static List<FixedPluginFilter> getPluginFilters() {
|
||||
if (fixedPluginFilterList == null) {
|
||||
synchronized (fixedPluginFilterMap) {
|
||||
if (fixedPluginFilterList == null) {
|
||||
Map<String, FixedPluginFilter> beansOfType = FizzGatewayApplication.appContext.getBeansOfType(FixedPluginFilter.class);
|
||||
if (beansOfType == null || beansOfType.isEmpty()) {
|
||||
fixedPluginFilterList = Collections.EMPTY_LIST;
|
||||
} else {
|
||||
fixedPluginFilterList = new ArrayList<>(beansOfType.values());
|
||||
Collections.sort(fixedPluginFilterList,
|
||||
(fa, fb) -> {
|
||||
return fa.getOrder() - fb.getOrder();
|
||||
}
|
||||
);
|
||||
fixedPluginFilterList.forEach(
|
||||
f -> {
|
||||
fixedPluginFilterMap.put(f.getId(), f);
|
||||
}
|
||||
);
|
||||
StringBuilder b = new StringBuilder(256);
|
||||
b.append("fixed plugin filters: \n");
|
||||
filters2sb(b);
|
||||
log.info(b.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fixedPluginFilterList;
|
||||
}
|
||||
|
||||
public abstract String getId();
|
||||
|
||||
public abstract int getOrder();
|
||||
|
||||
}
|
||||
49
src/main/java/com/wehotel/plugin/PluginConfig.java
Normal file
49
src/main/java/com/wehotel/plugin/PluginConfig.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.plugin;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.wehotel.util.JacksonUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public class PluginConfig {
|
||||
|
||||
public String plugin; // tb_plugin.eng_name
|
||||
|
||||
public String fixedConfig;
|
||||
|
||||
public Map<String/*tb_api_plugin_config.item*/, Object/*tb_api_plugin_config.value*/> config = Collections.EMPTY_MAP;
|
||||
|
||||
// @JsonProperty(value = "config", access = JsonProperty.Access.WRITE_ONLY)
|
||||
public void setConfig(String confJson) {
|
||||
if (StringUtils.isNotBlank(confJson)) {
|
||||
config = JacksonUtils.readValue(confJson, Map.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JacksonUtils.writeValueAsString(this);
|
||||
}
|
||||
}
|
||||
65
src/main/java/com/wehotel/plugin/PluginFilter.java
Normal file
65
src/main/java/com/wehotel/plugin/PluginFilter.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.plugin;
|
||||
|
||||
import com.wehotel.flume.clients.log4j2appender.LogService;
|
||||
import com.wehotel.filter.FilterResult;
|
||||
import com.wehotel.legacy.RespEntity;
|
||||
import com.wehotel.util.WebUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public abstract class PluginFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PluginFilter.class);
|
||||
|
||||
public Mono<Void> filter(ServerWebExchange exchange, Map<String, Object> config, String fixedConfig) {
|
||||
FilterResult pfr = WebUtils.getPrevFilterResult(exchange);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(this + ": " + pfr.id + " execute " + (pfr.success ? "success" : "fail"), LogService.BIZ_ID, exchange.getRequest().getId());
|
||||
}
|
||||
if (pfr.success) {
|
||||
return doFilter(exchange, config, fixedConfig);
|
||||
} else {
|
||||
if (WebUtils.getDirectResponse(exchange) == null) { // should not reach here
|
||||
ServerHttpRequest clientReq = exchange.getRequest();
|
||||
String rid = clientReq.getId();
|
||||
String msg = pfr.id + " fail";
|
||||
if (pfr.cause == null) {
|
||||
log.error(msg, LogService.BIZ_ID, rid);
|
||||
} else {
|
||||
log.error(msg, LogService.BIZ_ID, rid, pfr.cause);
|
||||
}
|
||||
return WebUtils.buildJsonDirectResponseAndBindContext(exchange, HttpStatus.OK, null, RespEntity.toJson(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg, rid));
|
||||
} else {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Mono<Void> doFilter(ServerWebExchange exchange, Map<String, Object> config, String fixedConfig);
|
||||
}
|
||||
91
src/main/java/com/wehotel/plugin/auth/ApiConfig.java
Normal file
91
src/main/java/com/wehotel/plugin/auth/ApiConfig.java
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.plugin.auth;
|
||||
|
||||
import com.wehotel.plugin.PluginConfig;
|
||||
import com.wehotel.util.Constants;
|
||||
import com.wehotel.util.JacksonUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public class ApiConfig {
|
||||
|
||||
public static final int DELETED = 1;
|
||||
|
||||
public static final char ALLOW = 'a';
|
||||
|
||||
public static final char FORBID = 'f';
|
||||
|
||||
public int id; // tb_api_auth.id
|
||||
|
||||
public int isDeleted = 0; // tb_api_auth.is_deleted
|
||||
|
||||
public char gatewayGroup; // tb_api_auth.gateway_group
|
||||
|
||||
public String service;
|
||||
|
||||
public HttpMethod method = HttpMethod.X;
|
||||
|
||||
public String path = String.valueOf(Constants.Symbol.FORWARD_SLASH);
|
||||
|
||||
private String app;
|
||||
|
||||
public char access = ALLOW;
|
||||
|
||||
public List<PluginConfig> pluginConfigs;
|
||||
|
||||
public void setApp(String a) {
|
||||
app = a;
|
||||
}
|
||||
|
||||
public String app() {
|
||||
if (StringUtils.isBlank(app)) {
|
||||
if (gatewayGroup == GatewayGroup.C) {
|
||||
app = App.TO_C;
|
||||
} else if (gatewayGroup == GatewayGroup.B) {
|
||||
app = App.TO_B;
|
||||
} else {
|
||||
throw new RuntimeException(toString() + " no app", null, false, false) {};
|
||||
}
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
public void setPath(String p) {
|
||||
if (StringUtils.isNotBlank(p)) {
|
||||
path = p;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMethod(String m) {
|
||||
method = HttpMethod.resolve(m);
|
||||
if (method == null) {
|
||||
method = HttpMethod.X;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JacksonUtils.writeValueAsString(this);
|
||||
}
|
||||
}
|
||||
332
src/main/java/com/wehotel/plugin/auth/ApiConfigService.java
Normal file
332
src/main/java/com/wehotel/plugin/auth/ApiConfigService.java
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.plugin.auth;
|
||||
|
||||
import com.ctrip.framework.apollo.model.ConfigChange;
|
||||
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
|
||||
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
|
||||
import com.wehotel.flume.clients.log4j2appender.LogService;
|
||||
import com.wehotel.config.SystemConfig;
|
||||
import com.wehotel.listener.AggregateRedisConfig;
|
||||
import com.wehotel.util.*;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class ApiConfigService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ApiConfigService.class);
|
||||
|
||||
private static final String fizzApiConfig = "fizz_api_config";
|
||||
|
||||
private static final String fizzApiConfigChannel = "fizz_api_config_channel";
|
||||
|
||||
private static final String signHeader = "fizz-sign";
|
||||
|
||||
private static final String timestampHeader = "fizz-ts";
|
||||
|
||||
private static final String secretKeyHeader = "fizz-secretkey";
|
||||
|
||||
private Map<String, GatewayGroup> app2gatewayGroupMap = new HashMap<>(32);
|
||||
|
||||
private Map<Integer, ApiConfig> apiConfigMap = new HashMap<>(128);
|
||||
|
||||
// TODO XXX
|
||||
@Value("${serviceWhiteList:x}")
|
||||
private String serviceWhiteList;
|
||||
private Set<String> whiteListSet = new HashSet<>(196);
|
||||
@ApolloConfigChangeListener
|
||||
private void configChangeListter(ConfigChangeEvent cce) {
|
||||
cce.changedKeys().forEach(
|
||||
k -> {
|
||||
ConfigChange cc = cce.getChange(k);
|
||||
if (cc.getPropertyName().equalsIgnoreCase("serviceWhiteList")) {
|
||||
log.info("old service white list: " + cc.getOldValue());
|
||||
serviceWhiteList = cc.getNewValue();
|
||||
afterServiceWhiteListSet();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
public void afterServiceWhiteListSet() {
|
||||
if (StringUtils.isNotBlank(serviceWhiteList)) {
|
||||
whiteListSet.clear();
|
||||
Arrays.stream(StringUtils.split(serviceWhiteList, Constants.Symbol.COMMA)).forEach(s -> {
|
||||
whiteListSet.add(s);
|
||||
});
|
||||
log.info("new service white list: " + whiteListSet.toString());
|
||||
} else {
|
||||
log.info("no service white list");
|
||||
}
|
||||
}
|
||||
|
||||
@Resource(name = AggregateRedisConfig.AGGREGATE_REACTIVE_REDIS_TEMPLATE)
|
||||
private ReactiveStringRedisTemplate rt;
|
||||
|
||||
@Resource
|
||||
private SystemConfig systemConfig;
|
||||
|
||||
@Resource
|
||||
private AppService appService;
|
||||
|
||||
@Autowired(required = false)
|
||||
private CustomAuth customAuth;
|
||||
|
||||
@PostConstruct
|
||||
public void init() throws Throwable {
|
||||
|
||||
afterServiceWhiteListSet(); // TODO XXX
|
||||
|
||||
final Throwable[] throwable = new Throwable[1];
|
||||
Throwable error = Mono.just(Objects.requireNonNull(rt.opsForHash().entries(fizzApiConfig)
|
||||
.defaultIfEmpty(new AbstractMap.SimpleEntry<>(ReactorUtils.OBJ, ReactorUtils.OBJ)).onErrorStop().doOnError(t -> {
|
||||
log.info(null, t);
|
||||
})
|
||||
.concatMap(e -> {
|
||||
Object k = e.getKey();
|
||||
if (k == ReactorUtils.OBJ) {
|
||||
return Flux.just(e);
|
||||
}
|
||||
Object v = e.getValue();
|
||||
log.info(k.toString() + Constants.Symbol.COLON + v.toString(), LogService.BIZ_ID, k.toString());
|
||||
String json = (String) v;
|
||||
try {
|
||||
ApiConfig ac = JacksonUtils.readValue(json, ApiConfig.class);
|
||||
apiConfigMap.put(ac.id, ac);
|
||||
updateApp2gatewayGroupMap(ac);
|
||||
return Flux.just(e);
|
||||
} catch (Throwable t) {
|
||||
throwable[0] = t;
|
||||
log.info(json, t);
|
||||
return Flux.error(t);
|
||||
}
|
||||
}).blockLast())).flatMap(
|
||||
e -> {
|
||||
if (throwable[0] != null) {
|
||||
return Mono.error(throwable[0]);
|
||||
}
|
||||
return lsnApiConfigChange();
|
||||
}
|
||||
).block();
|
||||
if (error != ReactorUtils.EMPTY_THROWABLE) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public Mono<Throwable> lsnApiConfigChange() {
|
||||
final Throwable[] throwable = new Throwable[1];
|
||||
final boolean[] b = {false};
|
||||
rt.listenToChannel(fizzApiConfigChannel).doOnError(t -> {
|
||||
throwable[0] = t;
|
||||
b[0] = false;
|
||||
log.error("lsn " + fizzApiConfigChannel, t);
|
||||
}).doOnSubscribe(
|
||||
s -> {
|
||||
b[0] = true;
|
||||
log.info("success to lsn on " + fizzApiConfigChannel);
|
||||
}
|
||||
).doOnNext(msg -> {
|
||||
String json = msg.getMessage();
|
||||
log.info(json, LogService.BIZ_ID, "acc" + System.currentTimeMillis());
|
||||
try {
|
||||
ApiConfig ac = JacksonUtils.readValue(json, ApiConfig.class);
|
||||
ApiConfig r = apiConfigMap.remove(ac.id);
|
||||
if (ac.isDeleted != ApiConfig.DELETED && r != null) {
|
||||
r.isDeleted = ApiConfig.DELETED;
|
||||
updateApp2gatewayGroupMap(r);
|
||||
}
|
||||
updateApp2gatewayGroupMap(ac);
|
||||
if (ac.isDeleted != ApiConfig.DELETED) {
|
||||
apiConfigMap.put(ac.id, ac);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.info(json, t);
|
||||
}
|
||||
}).subscribe();
|
||||
Throwable t = throwable[0];
|
||||
while (!b[0]) {
|
||||
if (t != null) {
|
||||
return Mono.error(t);
|
||||
} else {
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
} catch (InterruptedException e) {
|
||||
return Mono.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Mono.just(ReactorUtils.EMPTY_THROWABLE);
|
||||
}
|
||||
|
||||
private void updateApp2gatewayGroupMap(ApiConfig ac) {
|
||||
GatewayGroup gg = app2gatewayGroupMap.get(ac.app());
|
||||
if (ac.isDeleted == ApiConfig.DELETED) {
|
||||
if (gg == null) {
|
||||
log.info("no gateway group for " + ac.app());
|
||||
} else {
|
||||
gg.remove(ac);
|
||||
if (gg.getServiceConfigMap().isEmpty()) {
|
||||
app2gatewayGroupMap.remove(ac.app());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (gg == null) {
|
||||
gg = new GatewayGroup(ac.gatewayGroup);
|
||||
app2gatewayGroupMap.put(ac.app(), gg);
|
||||
gg.add(ac);
|
||||
} else {
|
||||
gg.update(ac);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum Access {
|
||||
|
||||
YES (null),
|
||||
|
||||
CANT_ACCESS_CURRENT_GATEWAY_GROUP ("cant access current gateway group"),
|
||||
|
||||
NO_GATEWAY_GROUP_FOR_APP ("no gateway group for app"),
|
||||
|
||||
NO_APP_CONFIG_FOR_APP ("no app config for app"),
|
||||
|
||||
ORIGIN_IP_NOT_IN_WHITE_LIST ("origin ip not in white list"),
|
||||
|
||||
NO_TIMESTAMP_OR_SIGN ("no timestamp or sign"),
|
||||
|
||||
SIGN_INVALID ("sign invalid"),
|
||||
|
||||
NO_CUSTOM_AUTH ("no custom auth"),
|
||||
|
||||
CUSTOM_AUTH_REJECT ("custom auth reject"),
|
||||
|
||||
SERVICE_NOT_OPEN ("service not open"),
|
||||
|
||||
NO_SERVICE_EXPOSE_TO_APP ("no service expose to app"),
|
||||
|
||||
SERVICE_API_NOT_EXPOSE_TO_APP ("service api not expose to app"),
|
||||
|
||||
CANT_ACCESS_SERVICE_API ("cant access service api");
|
||||
|
||||
private String reason;
|
||||
|
||||
Access(String r) {
|
||||
reason = r;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
|
||||
public Mono<Object> canAccess(ServerWebExchange exchange) {
|
||||
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
HttpHeaders hdrs = req.getHeaders();
|
||||
LogService.setBizId(req.getId());
|
||||
return canAccess(exchange, WebUtils.getAppId(exchange), WebUtils.getOriginIp(exchange), hdrs.getFirst(timestampHeader), hdrs.getFirst(signHeader), hdrs.getFirst(secretKeyHeader),
|
||||
WebUtils.getServiceId(exchange), req.getMethod(), WebUtils.getReqPath(exchange));
|
||||
}
|
||||
|
||||
private Mono<Object> canAccess(ServerWebExchange exchange, String app, String ip, String timestamp, String sign, String secretKey,
|
||||
String service, HttpMethod method, String path) {
|
||||
|
||||
GatewayGroup gg = app2gatewayGroupMap.get(app); boolean toCorBapp = App.TO_C.equals(app) || App.TO_B.equals(app);
|
||||
|
||||
if (gg == null) {
|
||||
if (toCorBapp) { return Mono.just(Access.YES); } else { return logWarnAndResult("no gateway group for " + app, Access.NO_GATEWAY_GROUP_FOR_APP); }
|
||||
} else {
|
||||
Set<Character> currentServerGatewayGroupSet = systemConfig.getCurrentServerGatewayGroupSet();
|
||||
if (currentServerGatewayGroupSet.contains(gg.id)) {
|
||||
Mono<Access> am = Mono.just(Access.YES);
|
||||
App a = appService.getApp(app);
|
||||
if (a == null) {
|
||||
if (!toCorBapp) { return logWarnAndResult("no app config for " + app, Access.NO_APP_CONFIG_FOR_APP); }
|
||||
} else if (a.useWhiteList && !a.ips.contains(ip)) {
|
||||
return logWarnAndResult(ip + " not in " + app + " white list", Access.ORIGIN_IP_NOT_IN_WHITE_LIST);
|
||||
} else if (a.useAuth) {
|
||||
if (a.authType == App.SIGN_AUTH) {
|
||||
if (StringUtils.isBlank(timestamp) || StringUtils.isBlank(sign)) { return logWarnAndResult(app + " lack timestamp " + timestamp + " or sign " + sign, Access.NO_TIMESTAMP_OR_SIGN); }
|
||||
else if (!validate(app, timestamp, a.secretkey, sign)) { return logWarnAndResult(app + " sign " + sign + " invalid", Access.SIGN_INVALID); }
|
||||
} else if (customAuth == null) {
|
||||
return logWarnAndResult(app + " no custom auth", Access.NO_CUSTOM_AUTH);
|
||||
} else {
|
||||
am = customAuth.auth(exchange, app, ip, timestamp, sign, secretKey, a);
|
||||
}
|
||||
}
|
||||
return am.flatMap(
|
||||
v -> {
|
||||
LogService.setBizId(exchange.getRequest().getId());
|
||||
if (v == Access.CUSTOM_AUTH_REJECT || v != Access.YES) { return Mono.just(Access.CUSTOM_AUTH_REJECT); }
|
||||
if (!whiteListSet.contains(service)) { return Mono.just(Access.SERVICE_NOT_OPEN); } // TODO XXX
|
||||
ServiceConfig sc = gg.getServiceConfig(service);
|
||||
if (sc == null) {
|
||||
if (toCorBapp) { return Mono.just(Access.YES); } else { return logWarnAndResult("no service expose to " + app, Access.NO_SERVICE_EXPOSE_TO_APP); }
|
||||
} else {
|
||||
ApiConfig ac = sc.getApiConfig(method, path);
|
||||
if (ac == null) {
|
||||
if (toCorBapp) { return Mono.just(Access.YES); } else { return logWarnAndResult(service + ' ' + method.name() + ' ' + path + " not expose to " + app, Access.SERVICE_API_NOT_EXPOSE_TO_APP); }
|
||||
} else if (ac.access == ApiConfig.ALLOW) {
|
||||
return Mono.just(ac);
|
||||
} else {
|
||||
return logWarnAndResult(app + " cant access " + service + ' ' + method.name() + ' ' + path, Access.CANT_ACCESS_SERVICE_API);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
} else {
|
||||
return logWarnAndResult(app + " cant access " + currentServerGatewayGroupSet, Access.CANT_ACCESS_CURRENT_GATEWAY_GROUP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Mono logWarnAndResult(String msg, Access access) {
|
||||
log.warn(msg);
|
||||
return Mono.just(access);
|
||||
}
|
||||
|
||||
private static boolean validate(String app, String timestamp, String secretKey, String sign) {
|
||||
StringBuilder b = new StringBuilder(128);
|
||||
b.append(app).append(Constants.Symbol.UNDERLINE).append(timestamp).append(Constants.Symbol.UNDERLINE).append(secretKey);
|
||||
return sign.equals(DigestUtils.md532(b.toString()));
|
||||
}
|
||||
|
||||
public Map<String, GatewayGroup> getApp2gatewayGroupMap() {
|
||||
return app2gatewayGroupMap;
|
||||
}
|
||||
}
|
||||
83
src/main/java/com/wehotel/plugin/auth/App.java
Normal file
83
src/main/java/com/wehotel/plugin/auth/App.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.plugin.auth;
|
||||
|
||||
import com.wehotel.util.JacksonUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public class App {
|
||||
|
||||
public static final String TO_C = "2c";
|
||||
|
||||
public static final String TO_B = "2b";
|
||||
|
||||
public static final int DELETED = 1;
|
||||
|
||||
public static final int SIGN_AUTH = 1;
|
||||
|
||||
public static final int CUSTOM_AUTH = 2;
|
||||
|
||||
public int isDeleted = 0; // tb_app_auth.is_deleted
|
||||
|
||||
public int id; // tb_app_auth.id
|
||||
|
||||
public String app; // tb_app_auth.app
|
||||
|
||||
public String name; // tb_app_auth.app_name
|
||||
|
||||
public boolean useAuth = false; // 0:false, 1:true
|
||||
|
||||
public int authType;
|
||||
|
||||
public String secretkey;
|
||||
|
||||
public boolean useWhiteList = false;
|
||||
|
||||
public Set<String> ips = Collections.emptySet();
|
||||
|
||||
public void setUseAuth(int i) {
|
||||
if (i == 1) {
|
||||
useAuth = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void setUseWhiteList(int i) {
|
||||
if (i == 1) {
|
||||
useWhiteList = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void setIps(String ips) {
|
||||
if (StringUtils.isNotBlank(ips)) {
|
||||
this.ips = Arrays.stream(StringUtils.split(ips, ',')).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JacksonUtils.writeValueAsString(this);
|
||||
}
|
||||
}
|
||||
163
src/main/java/com/wehotel/plugin/auth/AppService.java
Normal file
163
src/main/java/com/wehotel/plugin/auth/AppService.java
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.plugin.auth;
|
||||
|
||||
import com.wehotel.flume.clients.log4j2appender.LogService;
|
||||
import com.wehotel.listener.AggregateRedisConfig;
|
||||
import com.wehotel.util.Constants;
|
||||
import com.wehotel.util.JacksonUtils;
|
||||
import com.wehotel.util.ReactorUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class AppService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AppService.class);
|
||||
|
||||
private static final String fizzApp = "fizz_app";
|
||||
|
||||
private static final String fizzAppChannel = "fizz_app_channel";
|
||||
|
||||
private Map<String, App> appMap = new HashMap<>(32);
|
||||
|
||||
private Map<Integer, App> oldAppMap = new HashMap<>(32);
|
||||
|
||||
@Resource(name = AggregateRedisConfig.AGGREGATE_REACTIVE_REDIS_TEMPLATE)
|
||||
private ReactiveStringRedisTemplate rt;
|
||||
|
||||
@PostConstruct
|
||||
public void init() throws Throwable {
|
||||
final Throwable[] throwable = new Throwable[1];
|
||||
Throwable error = Mono.just(Objects.requireNonNull(rt.opsForHash().entries(fizzApp)
|
||||
.defaultIfEmpty(new AbstractMap.SimpleEntry<>(ReactorUtils.OBJ, ReactorUtils.OBJ)).onErrorStop().doOnError(t -> {
|
||||
log.info(null, t);
|
||||
})
|
||||
.concatMap(e -> {
|
||||
Object k = e.getKey();
|
||||
if (k == ReactorUtils.OBJ) {
|
||||
return Flux.just(e);
|
||||
}
|
||||
Object v = e.getValue();
|
||||
log.info(k.toString() + Constants.Symbol.COLON + v.toString(), LogService.BIZ_ID, k.toString());
|
||||
String json = (String) v;
|
||||
try {
|
||||
App app = JacksonUtils.readValue(json, App.class);
|
||||
oldAppMap.put(app.id, app);
|
||||
updateAppMap(app);
|
||||
return Flux.just(e);
|
||||
} catch (Throwable t) {
|
||||
throwable[0] = t;
|
||||
log.info(json, t);
|
||||
return Flux.error(t);
|
||||
}
|
||||
}).blockLast())).flatMap(
|
||||
e -> {
|
||||
if (throwable[0] != null) {
|
||||
return Mono.error(throwable[0]);
|
||||
}
|
||||
return lsnAppChange();
|
||||
}
|
||||
).block();
|
||||
if (error != ReactorUtils.EMPTY_THROWABLE) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private Mono<Throwable> lsnAppChange() {
|
||||
final Throwable[] throwable = new Throwable[1];
|
||||
final boolean[] b = {false};
|
||||
rt.listenToChannel(fizzAppChannel).doOnError(t -> {
|
||||
throwable[0] = t;
|
||||
b[0] = false;
|
||||
log.error("lsn " + fizzAppChannel, t);
|
||||
}).doOnSubscribe(
|
||||
s -> {
|
||||
b[0] = true;
|
||||
log.info("success to lsn on " + fizzAppChannel);
|
||||
}
|
||||
).doOnNext(msg -> {
|
||||
String json = msg.getMessage();
|
||||
log.info(json, LogService.BIZ_ID, "ac" + System.currentTimeMillis());
|
||||
try {
|
||||
App app = JacksonUtils.readValue(json, App.class);
|
||||
App r = oldAppMap.remove(app.id);
|
||||
if (app.isDeleted != App.DELETED && r != null) {
|
||||
appMap.remove(r.app);
|
||||
}
|
||||
updateAppMap(app);
|
||||
if (app.isDeleted != App.DELETED) {
|
||||
oldAppMap.put(app.id, app);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.info(json, t);
|
||||
}
|
||||
}).subscribe();
|
||||
Throwable t = throwable[0];
|
||||
while (!b[0]) {
|
||||
if (t != null) {
|
||||
return Mono.error(t);
|
||||
} else {
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
} catch (InterruptedException e) {
|
||||
return Mono.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Mono.just(ReactorUtils.EMPTY_THROWABLE);
|
||||
}
|
||||
|
||||
private void updateAppMap(App app) {
|
||||
if (app.isDeleted == App.DELETED) {
|
||||
App removedApp = appMap.remove(app.app);
|
||||
log.info("remove " + removedApp);
|
||||
} else {
|
||||
App existApp = appMap.get(app.app);
|
||||
appMap.put(app.app, app);
|
||||
if (existApp == null) {
|
||||
log.info("add " + app);
|
||||
} else {
|
||||
log.info("update " + existApp + " with " + app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public App getApp(String app) {
|
||||
return appMap.get(app);
|
||||
}
|
||||
|
||||
public Map<String, App> getAppMap() {
|
||||
return appMap;
|
||||
}
|
||||
}
|
||||
61
src/main/java/com/wehotel/plugin/auth/AuthPluginFilter.java
Normal file
61
src/main/java/com/wehotel/plugin/auth/AuthPluginFilter.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.plugin.auth;
|
||||
|
||||
import com.wehotel.flume.clients.log4j2appender.LogService;
|
||||
import com.wehotel.plugin.PluginFilter;
|
||||
import com.wehotel.util.WebUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
@Component(AuthPluginFilter.AUTH_PLUGIN_FILTER)
|
||||
public class AuthPluginFilter extends PluginFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AuthPluginFilter.class);
|
||||
|
||||
public static final String AUTH_PLUGIN_FILTER = "authPlugin";
|
||||
|
||||
public static final String RESULT = "result";
|
||||
|
||||
@Resource
|
||||
private ApiConfigService apiConfigService;
|
||||
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, Map<String, Object> config, String fixedConfig) {
|
||||
return apiConfigService.canAccess(exchange).flatMap(
|
||||
r -> {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("req auth: " + r, LogService.BIZ_ID, exchange.getRequest().getId());
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>(1, 1.0f);
|
||||
data.put(RESULT, r);
|
||||
return WebUtils.transmitSuccessFilterResultAndEmptyMono(exchange, AUTH_PLUGIN_FILTER, data);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
32
src/main/java/com/wehotel/plugin/auth/CustomAuth.java
Normal file
32
src/main/java/com/wehotel/plugin/auth/CustomAuth.java
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.plugin.auth;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public interface CustomAuth {
|
||||
|
||||
/**
|
||||
* 认证通过返回 Mono<Access.YES>, 不通过返回 Mono<Access.CUSTOM_AUTH_REJECT>
|
||||
*/
|
||||
Mono<ApiConfigService.Access> auth(ServerWebExchange exchange, String appId, String ip, String timestamp, String sign, String secretKey, App fizzAppConfig);
|
||||
}
|
||||
83
src/main/java/com/wehotel/plugin/auth/GatewayGroup.java
Normal file
83
src/main/java/com/wehotel/plugin/auth/GatewayGroup.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.plugin.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public class GatewayGroup {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GatewayGroup.class);
|
||||
|
||||
public static final char C = 'c';
|
||||
|
||||
public static final char B = 'b';
|
||||
|
||||
public static final char T = 't';
|
||||
|
||||
public char id;
|
||||
|
||||
private Map<String, ServiceConfig> serviceConfigMap = new HashMap<>(128);
|
||||
|
||||
public GatewayGroup(char id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Map<String, ServiceConfig> getServiceConfigMap() {
|
||||
return serviceConfigMap;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public ServiceConfig getServiceConfig(String id) {
|
||||
return serviceConfigMap.get(id);
|
||||
}
|
||||
|
||||
public void remove(ApiConfig ac) {
|
||||
ServiceConfig sc = serviceConfigMap.get(ac.service);
|
||||
if (sc == null) {
|
||||
log.info("no service config for " + ac);
|
||||
} else {
|
||||
sc.remove(ac);
|
||||
if (sc.apiConfigMap().isEmpty()) {
|
||||
serviceConfigMap.remove(ac.service);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void add(ApiConfig ac) {
|
||||
ServiceConfig sc = new ServiceConfig(ac.service);
|
||||
serviceConfigMap.put(ac.service, sc);
|
||||
sc.add(ac);
|
||||
}
|
||||
|
||||
public void update(ApiConfig ac) {
|
||||
ServiceConfig sc = serviceConfigMap.get(ac.service);
|
||||
if (sc == null) {
|
||||
add(ac);
|
||||
} else {
|
||||
sc.update(ac);
|
||||
}
|
||||
}
|
||||
}
|
||||
125
src/main/java/com/wehotel/plugin/auth/ServiceConfig.java
Normal file
125
src/main/java/com/wehotel/plugin/auth/ServiceConfig.java
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.plugin.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.wehotel.util.Constants;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public class ServiceConfig {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ServiceConfig.class);
|
||||
|
||||
private static final String forward_slash_str = String.valueOf(Constants.Symbol.FORWARD_SLASH);
|
||||
|
||||
public String id;
|
||||
|
||||
private Map<Integer, ApiConfig> apiConfigMap = new HashMap<>(32);
|
||||
|
||||
private Map<String, EnumMap<HttpMethod, ApiConfig>> path2methodToApiConfigMapMap = new HashMap<>(32);
|
||||
|
||||
public ServiceConfig(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Map<Integer, ApiConfig> apiConfigMap() {
|
||||
return apiConfigMap;
|
||||
}
|
||||
|
||||
public Map<String, EnumMap<HttpMethod, ApiConfig>> getPath2methodToApiConfigMapMap() {
|
||||
return path2methodToApiConfigMapMap;
|
||||
}
|
||||
|
||||
public void remove(ApiConfig ac) {
|
||||
ApiConfig remove = apiConfigMap.remove(ac.id);
|
||||
log.info(remove + " is removed from api config map");
|
||||
Map<HttpMethod, ApiConfig> method2apiConfigMap = path2methodToApiConfigMapMap.get(ac.path);
|
||||
if (method2apiConfigMap != null) {
|
||||
ApiConfig r = method2apiConfigMap.remove(ac.method);
|
||||
log.info(r + " is removed from method 2 api config map");
|
||||
if (method2apiConfigMap.isEmpty()) {
|
||||
path2methodToApiConfigMapMap.remove(ac.path);
|
||||
}
|
||||
} else {
|
||||
log.info("no method 2 api config map for " + ac.path);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(ApiConfig ac) {
|
||||
apiConfigMap.put(ac.id, ac);
|
||||
EnumMap<HttpMethod, ApiConfig> method2apiConfigMap = path2methodToApiConfigMapMap.get(ac.path);
|
||||
if (method2apiConfigMap == null) {
|
||||
method2apiConfigMap = new EnumMap<>(HttpMethod.class);
|
||||
path2methodToApiConfigMapMap.put(ac.path, method2apiConfigMap);
|
||||
}
|
||||
method2apiConfigMap.put(ac.method, ac);
|
||||
log.info(ac + " is added to api config map");
|
||||
}
|
||||
|
||||
public void update(ApiConfig ac) {
|
||||
ApiConfig prev = apiConfigMap.put(ac.id, ac);
|
||||
log.info(prev + " is updated by " + ac + " in api config map");
|
||||
EnumMap<HttpMethod, ApiConfig> method2apiConfigMap = path2methodToApiConfigMapMap.get(ac.path);
|
||||
if (method2apiConfigMap == null) {
|
||||
method2apiConfigMap = new EnumMap<>(HttpMethod.class);
|
||||
path2methodToApiConfigMapMap.put(ac.path, method2apiConfigMap);
|
||||
}
|
||||
ApiConfig put = method2apiConfigMap.put(ac.method, ac);
|
||||
log.info(put + " is updated by " + ac + " in method 2 api config map");
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public ApiConfig getApiConfig(HttpMethod method, String path) {
|
||||
while (true) {
|
||||
EnumMap<HttpMethod, ApiConfig> method2apiConfigMap = path2methodToApiConfigMapMap.get(path);
|
||||
if (method2apiConfigMap == null) {
|
||||
int i = path.lastIndexOf(Constants.Symbol.FORWARD_SLASH);
|
||||
if (i == 0) {
|
||||
method2apiConfigMap = path2methodToApiConfigMapMap.get(forward_slash_str);
|
||||
if (method2apiConfigMap == null) {
|
||||
return null;
|
||||
} else {
|
||||
return getApiConfig0(method, method2apiConfigMap);
|
||||
}
|
||||
} else {
|
||||
path = path.substring(0, i);
|
||||
}
|
||||
} else {
|
||||
return getApiConfig0(method, method2apiConfigMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ApiConfig getApiConfig0(HttpMethod method, EnumMap<HttpMethod, ApiConfig> method2apiConfigMap) {
|
||||
ApiConfig ac = method2apiConfigMap.get(method);
|
||||
if (ac == null) {
|
||||
return method2apiConfigMap.get(HttpMethod.X);
|
||||
} else {
|
||||
return ac;
|
||||
}
|
||||
}
|
||||
}
|
||||
101
src/main/java/com/wehotel/plugin/stat/StatPluginFilter.java
Normal file
101
src/main/java/com/wehotel/plugin/stat/StatPluginFilter.java
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.plugin.stat;
|
||||
|
||||
import com.wehotel.flume.clients.log4j2appender.LogService;
|
||||
import com.wehotel.plugin.PluginFilter;
|
||||
import com.wehotel.util.Constants;
|
||||
import com.wehotel.util.ThreadContext;
|
||||
import com.wehotel.util.WebUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
@Component(StatPluginFilter.STAT_PLUGIN_FILTER)
|
||||
public class StatPluginFilter extends PluginFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(StatPluginFilter.class);
|
||||
|
||||
public static final String STAT_PLUGIN_FILTER = "statPlugin";
|
||||
|
||||
private static final String accessStat = "$accessStat";
|
||||
|
||||
private static final String ip = "\"ip\":";
|
||||
|
||||
private static final String gatewayGroup = "\"gatewayGroup\":";
|
||||
|
||||
private static final String service = "\"service\":";
|
||||
|
||||
private static final String appid = "\"appid\":";
|
||||
|
||||
private static final String apiMethod = "\"apiMethod\":";
|
||||
|
||||
private static final String apiPath = "\"apiPath\":";
|
||||
|
||||
private static final String reqTime = "\"reqTime\":";
|
||||
|
||||
@Value("${stat.open:false}")
|
||||
private boolean statOpen = false;
|
||||
|
||||
@Value("${stat.topic:fizz_access_stat}")
|
||||
private String fizzAccessStatTopic;
|
||||
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, Map<String, Object> config, String fixedConfig) {
|
||||
|
||||
if (statOpen) {
|
||||
StringBuilder b = (StringBuilder) ThreadContext.get(accessStat);
|
||||
if (b == null) {
|
||||
b = new StringBuilder(128);
|
||||
ThreadContext.set(accessStat, b);
|
||||
} else {
|
||||
b.delete(0, b.length());
|
||||
}
|
||||
|
||||
b.append(Constants.Symbol.LEFT_BRACE);
|
||||
b.append(ip); toJsonStringValue(b, WebUtils.getOriginIp(exchange)); b.append(Constants.Symbol.COMMA);
|
||||
b.append(gatewayGroup); toJsonStringValue(b, WebUtils.getCurrentGatewayGroup(exchange)); b.append(Constants.Symbol.COMMA);
|
||||
b.append(service); toJsonStringValue(b, WebUtils.getServiceId(exchange)); b.append(Constants.Symbol.COMMA);
|
||||
b.append(appid); toJsonStringValue(b, WebUtils.getAppId(exchange)); b.append(Constants.Symbol.COMMA);
|
||||
b.append(apiMethod); toJsonStringValue(b, exchange.getRequest().getMethodValue()); b.append(Constants.Symbol.COMMA);
|
||||
b.append(apiPath); toJsonStringValue(b, WebUtils.getReqPath(exchange)); b.append(Constants.Symbol.COMMA);
|
||||
b.append(reqTime) .append(System.currentTimeMillis());
|
||||
b.append(Constants.Symbol.RIGHT_BRACE);
|
||||
|
||||
log.info(b.toString(), LogService.HANDLE_STGY, LogService.toKF(fizzAccessStatTopic));
|
||||
}
|
||||
|
||||
return WebUtils.transmitSuccessFilterResultAndEmptyMono(exchange, STAT_PLUGIN_FILTER, null);
|
||||
}
|
||||
|
||||
private static void toJsonStringValue(StringBuilder b, String value) {
|
||||
b.append(Constants.Symbol.DOUBLE_QUOTE).append(value).append(Constants.Symbol.DOUBLE_QUOTE);
|
||||
}
|
||||
|
||||
private static void toJsonStringValue(StringBuilder b, Character value) {
|
||||
b.append(Constants.Symbol.DOUBLE_QUOTE).append(value).append(Constants.Symbol.DOUBLE_QUOTE);
|
||||
}
|
||||
}
|
||||
40
src/main/java/com/wehotel/proxy/CallBackendConfig.java
Normal file
40
src/main/java/com/wehotel/proxy/CallBackendConfig.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.proxy;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
* 请求转发/调用后端接口时的负载均衡、流控、failover、超时等配置
|
||||
*/
|
||||
|
||||
public class CallBackendConfig {
|
||||
|
||||
// TODO to be continue
|
||||
|
||||
public static final long DEFAULT_TIME_OUT = 3000; // mills
|
||||
|
||||
public static final CallBackendConfig DEFAULT = new CallBackendConfig();
|
||||
|
||||
public long timeout = DEFAULT_TIME_OUT;
|
||||
|
||||
public CallBackendConfig() {
|
||||
}
|
||||
|
||||
public CallBackendConfig(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
}
|
||||
242
src/main/java/com/wehotel/proxy/FizzWebClient.java
Normal file
242
src/main/java/com/wehotel/proxy/FizzWebClient.java
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.proxy;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.discovery.shared.Applications;
|
||||
import com.wehotel.flume.clients.log4j2appender.LogService;
|
||||
import com.wehotel.config.AggrWebClientConfig;
|
||||
import com.wehotel.config.ProxyWebClientConfig;
|
||||
import com.wehotel.util.Constants;
|
||||
import com.wehotel.util.ThreadContext;
|
||||
import com.wehotel.util.WebUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class FizzWebClient {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(FizzWebClient.class);
|
||||
|
||||
private static final String aggrSend = "$aggrSend";
|
||||
|
||||
private static final String localhost = "localhost";
|
||||
|
||||
@Resource
|
||||
private EurekaClient eurekaClient;
|
||||
|
||||
@Resource(name = ProxyWebClientConfig.proxyWebClient)
|
||||
private WebClient proxyWebClient;
|
||||
|
||||
@Resource(name = AggrWebClientConfig.aggrWebClient)
|
||||
private WebClient aggrWebClient;
|
||||
|
||||
@Value("${fizz-web-client.timeout:-1}")
|
||||
private long timeout = -1;
|
||||
|
||||
@PostConstruct
|
||||
public void afterPropertiesSet() {
|
||||
if (timeout != -1) {
|
||||
CallBackendConfig.DEFAULT.timeout = timeout;
|
||||
}
|
||||
log.info("fizz web client timeout is " + CallBackendConfig.DEFAULT.timeout);
|
||||
}
|
||||
|
||||
public Mono<ClientResponse> aggrSend(String aggrService, HttpMethod aggrMethod, String aggrPath, @Nullable String originReqIdOrBizId,
|
||||
HttpMethod method, String uriOrSvc, @Nullable HttpHeaders headers, @Nullable Object body, @Nullable Long timeout) {
|
||||
|
||||
ThreadContext.set(aggrSend, Constants.Symbol.EMPTY); // TODO will be remove in future
|
||||
CallBackendConfig cbc = null;
|
||||
if (timeout != null) {
|
||||
cbc = new CallBackendConfig(timeout);
|
||||
}
|
||||
return aggrResolveAddressSend(aggrService, aggrMethod, aggrPath, originReqIdOrBizId, method, uriOrSvc, headers, body, cbc);
|
||||
}
|
||||
|
||||
public Mono<ClientResponse> aggrSend(String aggrService, HttpMethod aggrMethod, String aggrPath, @Nullable String originReqIdOrBizId,
|
||||
HttpMethod method, String uriOrSvc, @Nullable HttpHeaders headers, @Nullable Object body) {
|
||||
|
||||
ThreadContext.set(aggrSend, Constants.Symbol.EMPTY); // TODO will be remove in future
|
||||
return aggrResolveAddressSend(aggrService, aggrMethod, aggrPath, originReqIdOrBizId, method, uriOrSvc, headers, body, null);
|
||||
}
|
||||
|
||||
private Mono<ClientResponse> aggrResolveAddressSend(String aggrService, HttpMethod aggrMethod, String aggrPath, @Nullable String originReqIdOrBizId,
|
||||
HttpMethod method, String uriOrSvc, @Nullable HttpHeaders headers, @Nullable Object body, @Nullable CallBackendConfig cbc) {
|
||||
|
||||
String s = extractServiceOrAddress(uriOrSvc);
|
||||
if (isService(s)) {
|
||||
String path = uriOrSvc.substring(uriOrSvc.indexOf(Constants.Symbol.FORWARD_SLASH, 10));
|
||||
return send2service(originReqIdOrBizId, method, s, path, headers, body, cbc);
|
||||
} else {
|
||||
return send2uri(originReqIdOrBizId, method, uriOrSvc, headers, body, cbc);
|
||||
}
|
||||
}
|
||||
|
||||
public Mono<ClientResponse> proxySend2service(@Nullable String originReqIdOrBizId, HttpMethod method, String service, String relativeUri,
|
||||
@Nullable HttpHeaders headers, @Nullable Object body) {
|
||||
|
||||
return send2service(originReqIdOrBizId, method, service, relativeUri, headers, body, null);
|
||||
}
|
||||
|
||||
public Mono<ClientResponse> send2service(@Nullable String originReqIdOrBizId, HttpMethod method, String service, String relativeUri,
|
||||
@Nullable HttpHeaders headers, @Nullable Object body, @Nullable CallBackendConfig cbc) {
|
||||
|
||||
// TODO this the future
|
||||
// if (cbc == null) {
|
||||
// InstanceInfo inst = roundRobinChoose1instFrom(service);
|
||||
// String uri = buildUri(inst, relativeUri);
|
||||
// return send2uri(originReqIdOrBizId, method, uri, headers, body, null);
|
||||
// } else {
|
||||
// List<InstanceInfo> insts = eurekaClient.getInstancesByVipAddress(service, false);
|
||||
// // TODO 据callBackendConfig, 结合insts的实际metric, 从insts中选择合适的一个,转发请求过去
|
||||
// }
|
||||
// what about multiple nginx instance
|
||||
|
||||
// current
|
||||
InstanceInfo inst = roundRobinChoose1instFrom(service);
|
||||
String uri = buildUri(inst, relativeUri);
|
||||
return send2uri(originReqIdOrBizId, method, uri, headers, body, cbc);
|
||||
}
|
||||
|
||||
public Mono<ClientResponse> send2uri(@Nullable String originReqIdOrBizId, HttpMethod method, String uri,
|
||||
@Nullable HttpHeaders headers, @Nullable Object body, @Nullable Long timeout) {
|
||||
|
||||
CallBackendConfig cbc = null;
|
||||
if (timeout != null) {
|
||||
cbc = new CallBackendConfig(timeout);
|
||||
}
|
||||
return send2uri(originReqIdOrBizId, method, uri, headers, body, cbc);
|
||||
}
|
||||
|
||||
private static final String r = "R";
|
||||
|
||||
private Mono<ClientResponse> send2uri(@Nullable String originReqIdOrBizId, HttpMethod method, String uri,
|
||||
@Nullable HttpHeaders headers, @Nullable Object body, @Nullable CallBackendConfig cbc) {
|
||||
|
||||
if (originReqIdOrBizId == null) { // should not execute this
|
||||
if (headers == null) {
|
||||
originReqIdOrBizId = r + ThreadLocalRandom.current().nextInt(1_000, 10_000);
|
||||
} else {
|
||||
originReqIdOrBizId = r + headers.hashCode();
|
||||
}
|
||||
}
|
||||
final String reqId = originReqIdOrBizId;
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
StringBuilder b = ThreadContext.getStringBuilder();
|
||||
WebUtils.request2stringBuilder(reqId, method, uri, headers, null, b);
|
||||
log.debug(b.toString(), LogService.BIZ_ID, reqId);
|
||||
}
|
||||
|
||||
if (cbc == null) {
|
||||
cbc = CallBackendConfig.DEFAULT;
|
||||
}
|
||||
|
||||
// TODO remove this, and all event loop share one web client or one event loop one web client in future
|
||||
WebClient.RequestBodySpec req = (ThreadContext.remove(aggrSend) == null ? proxyWebClient : aggrWebClient).method(method).uri(uri).headers(
|
||||
hdrs -> {
|
||||
if (headers != null) {
|
||||
headers.forEach(
|
||||
(h, vs) -> {
|
||||
hdrs.addAll(h, vs);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (body != null) {
|
||||
if (body instanceof Flux) {
|
||||
Flux<DataBuffer> db = (Flux<DataBuffer>) body;
|
||||
req.body(BodyInserters.fromDataBuffers(db));
|
||||
} else if (body instanceof String) {
|
||||
String s = (String) body;
|
||||
req.body(Mono.just(s), String.class);
|
||||
} else {
|
||||
req.bodyValue(body);
|
||||
}
|
||||
}
|
||||
|
||||
Mono<ClientResponse> rm = req.exchange().name(reqId)
|
||||
.doOnRequest(i -> {})
|
||||
.doOnSuccess(r -> {})
|
||||
/*.doOnError(
|
||||
t -> {
|
||||
Schedulers.parallel().schedule(() -> {
|
||||
log.error("", LogService.BIZ_ID, reqId, t);
|
||||
});
|
||||
}
|
||||
)
|
||||
.timeout(Duration.ofMillis(cbc.timeout))*/
|
||||
;
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
rm = rm.log();
|
||||
}
|
||||
return rm;
|
||||
|
||||
// TODO 请求完成后,做metric, 以反哺后续的请求转发
|
||||
}
|
||||
|
||||
private String buildUri(InstanceInfo inst, String path) {
|
||||
StringBuilder b = ThreadContext.getStringBuilder();
|
||||
return b.append(Constants.Symbol.HTTP_PROTOCOL_PREFIX).append(inst.getIPAddr()).append(Constants.Symbol.COLON).append(inst.getPort()).append(path).toString();
|
||||
}
|
||||
|
||||
private InstanceInfo roundRobinChoose1instFrom(String service) {
|
||||
List<InstanceInfo> insts = eurekaClient.getInstancesByVipAddress(service, false);
|
||||
if (insts == null || insts.isEmpty()) {
|
||||
throw new RuntimeException("eureka no " + service, null, false, false) {};
|
||||
}
|
||||
Applications apps = eurekaClient.getApplications();
|
||||
int index = (int) (apps.getNextIndex(service.toUpperCase(), false).incrementAndGet() % insts.size());
|
||||
return insts.get(index);
|
||||
}
|
||||
|
||||
private String extractServiceOrAddress(String uriOrSvc) {
|
||||
return uriOrSvc.substring(7, uriOrSvc.indexOf(Constants.Symbol.FORWARD_SLASH, 10));
|
||||
}
|
||||
|
||||
private boolean isService(String s) {
|
||||
if (s.indexOf(Constants.Symbol.DOT) > 0 || s.equals(localhost)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
30
src/main/java/com/wehotel/proxy/package-info.java
Normal file
30
src/main/java/com/wehotel/proxy/package-info.java
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author lancer
|
||||
*
|
||||
* 设想的包结构:
|
||||
* com.wehotel.fizz
|
||||
* .config
|
||||
* .filter
|
||||
* .util
|
||||
* .aggr 聚合逻辑
|
||||
* .proxy 代理逻辑
|
||||
* .route 路由逻辑
|
||||
* .legacy
|
||||
*/
|
||||
package com.wehotel.proxy;
|
||||
98
src/main/java/com/wehotel/util/Constants.java
Normal file
98
src/main/java/com/wehotel/util/Constants.java
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.util;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public final class Constants {
|
||||
|
||||
public static final class Symbol {
|
||||
public static final String EMPTY = "";
|
||||
public static final String SPACE_STR = " ";
|
||||
public static final String TWO_SPACE_STR = " ";
|
||||
public static final char COMMA = ',';
|
||||
public static final char COLON = ':';
|
||||
public static final char BLANK = ' ';
|
||||
public static final char SPACE = BLANK;
|
||||
public static final char FORWARD_SLASH = '/';
|
||||
public static final char BACK_SLASH = '\\';
|
||||
public static final char DOT = '.';
|
||||
public static final char SEMICOLON = ';';
|
||||
public static final char QUESTION = '?';
|
||||
public static final char DOUBLE_QUOTE = '"';
|
||||
public static final char SINGLE_QUOTE = '\'';
|
||||
public static final char ASTERISK = '*';
|
||||
public static final char DASH = '-';
|
||||
public static final char UNDERLINE = '_';
|
||||
public static final char EQUAL = '=';
|
||||
public static final char AT = '@';
|
||||
public static final char HASH = '#';
|
||||
public static final char LEFT_SQUARE_BRACKET = '[';
|
||||
public static final char RIGHT_SQUARE_BRACKET = ']';
|
||||
public static final char LEFT_BRACE = '{';
|
||||
public static final char RIGHT_BRACE = '}';
|
||||
public static final String LINE_SEPARATOR = System.lineSeparator();
|
||||
public static final String COMMA_SPACE = ", ";
|
||||
public static final char LF = '\n';
|
||||
public static final char TAB = '\t';
|
||||
public static final char NUL = '\u0000';
|
||||
public static final String HTTP_PROTOCOL_PREFIX = "http://";
|
||||
|
||||
static final char c0 = SystemUtils.IS_OS_WINDOWS ? Constants.Symbol.BACK_SLASH : Constants.Symbol.FORWARD_SLASH;
|
||||
public static final char PATH_SEPARATOR = c0;
|
||||
}
|
||||
|
||||
public static final class Charset {
|
||||
public static final String UTF8 = "UTF-8";
|
||||
public static final String GBK = "GBK";
|
||||
public static final String ISO88591 = "ISO8859-1";
|
||||
}
|
||||
|
||||
public static final class DatetimePattern {
|
||||
public static final String DP10 = "yyyy-MM-dd";
|
||||
public static final String DP14 = "yyyyMMddHHmmss";
|
||||
public static final String DP19 = "yyyy-MM-dd HH:mm:ss";
|
||||
public static final String DP23 = "yyyy-MM-dd HH:mm:ss.SSS";
|
||||
public static final byte MILLS_LEN = 13;
|
||||
}
|
||||
|
||||
public static final class Profiles {
|
||||
public static final String LOCAL = "local";
|
||||
public static final String DEV = "dev";
|
||||
public static final String TEST = "test";
|
||||
public static final String PREPROD = "preprod";
|
||||
public static final String PRE = "pre";
|
||||
public static final String PROD = "prod";
|
||||
|
||||
public static final String HTTP_SERVER = "http_server";
|
||||
public static final String HTTP_CLIENT = "http_client";
|
||||
public static final String MYSQL = "mysql";
|
||||
public static final String REDIS = "redis";
|
||||
public static final String CODIS = "codis";
|
||||
public static final String MONGO = "mongo";
|
||||
public static final String ACTIVEMQ = "activemq";
|
||||
public static final String KAFKA = "kafka";
|
||||
public static final String ELASTICSEARCH = "elasticsearch";
|
||||
public static final String SCHED = "sched";
|
||||
}
|
||||
|
||||
public static final String BIZ_ID = "bizId";
|
||||
}
|
||||
186
src/main/java/com/wehotel/util/DateTimeUtils.java
Normal file
186
src/main/java/com/wehotel/util/DateTimeUtils.java
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.util;
|
||||
|
||||
import com.wehotel.util.Constants.DatetimePattern;
|
||||
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public abstract class DateTimeUtils {
|
||||
|
||||
private static Map<String, DateTimeFormatter> dateTimeFormatters = new HashMap<>();
|
||||
|
||||
private static ZoneId defaultZone = ZoneId.systemDefault();
|
||||
|
||||
private static final String zeroTimeSuffix = " 00:00:00";
|
||||
|
||||
public static DateTimeFormatter getDateTimeFormatter(String pattern) {
|
||||
DateTimeFormatter f = dateTimeFormatters.get(pattern);
|
||||
if (f == null) {
|
||||
f = DateTimeFormatter.ofPattern(pattern);
|
||||
dateTimeFormatters.put(pattern, f);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
public static Date from(Instant i) {
|
||||
return new Date(i.toEpochMilli());
|
||||
}
|
||||
|
||||
public static Date from(LocalDateTime ldt) {
|
||||
return from(ldt.atZone(defaultZone).toInstant());
|
||||
}
|
||||
|
||||
public static LocalDateTime from(Date d) {
|
||||
return LocalDateTime.ofInstant(d.toInstant(), defaultZone);
|
||||
}
|
||||
|
||||
public static LocalDateTime from(long l) {
|
||||
return LocalDateTime.ofInstant(Instant.ofEpochMilli(l), defaultZone);
|
||||
}
|
||||
|
||||
public static long toMillis(LocalDateTime ldt) {
|
||||
return ldt.atZone(defaultZone).toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
public static long toMillis(String dateTime, String... pattern) {
|
||||
if (dateTime.length() == 10) {
|
||||
dateTime += zeroTimeSuffix;
|
||||
}
|
||||
String p = DatetimePattern.DP19;
|
||||
if (pattern.length != 0) {
|
||||
p = pattern[0];
|
||||
}
|
||||
DateTimeFormatter f = getDateTimeFormatter(p);
|
||||
LocalDateTime ldt = LocalDateTime.parse(dateTime, f);
|
||||
return ldt.atZone(defaultZone).toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
public static String toDate(long mills, String... pattern) {
|
||||
String p = DatetimePattern.DP10;
|
||||
if (pattern.length != 0) {
|
||||
p = pattern[0];
|
||||
}
|
||||
LocalDateTime ldt = LocalDateTime.ofInstant(Instant.ofEpochMilli(mills), defaultZone);
|
||||
DateTimeFormatter f = getDateTimeFormatter(p);
|
||||
return ldt.format(f);
|
||||
}
|
||||
|
||||
public static long until(LocalDate thatDate) {
|
||||
return LocalDate.now().until(thatDate, ChronoUnit.DAYS);
|
||||
}
|
||||
|
||||
public static long from(LocalDate thatDate) {
|
||||
return thatDate.until(LocalDate.now(), ChronoUnit.DAYS);
|
||||
}
|
||||
|
||||
public static long until(LocalDate startDate, LocalDate endDate) {
|
||||
return startDate.until(endDate, ChronoUnit.DAYS);
|
||||
}
|
||||
|
||||
public static LocalDate date2localDate(Date date) {
|
||||
return date.toInstant().atZone(defaultZone).toLocalDate();
|
||||
}
|
||||
|
||||
public static Date localDate2date(LocalDate localDate) {
|
||||
ZonedDateTime zonedDateTime = localDate.atStartOfDay(defaultZone);
|
||||
return Date.from(zonedDateTime.toInstant());
|
||||
}
|
||||
|
||||
public static String localDate2str(LocalDate date, String... pattern) {
|
||||
String p = DatetimePattern.DP10;
|
||||
if (pattern.length != 0) {
|
||||
p = pattern[0];
|
||||
}
|
||||
DateTimeFormatter f = getDateTimeFormatter(p);
|
||||
return date.format(f);
|
||||
}
|
||||
|
||||
public static String localDateTime2str(LocalDateTime localDateTime, String... pattern) {
|
||||
String p = DatetimePattern.DP23;
|
||||
if (pattern.length != 0) {
|
||||
p = pattern[0];
|
||||
}
|
||||
DateTimeFormatter f = getDateTimeFormatter(p);
|
||||
return localDateTime.format(f);
|
||||
}
|
||||
|
||||
public static List<String> datesBetween(String start, String end) {
|
||||
LocalDate sd = LocalDate.parse(start);
|
||||
LocalDate ed = LocalDate.parse(end);
|
||||
long dist = ChronoUnit.DAYS.between(sd, ed);
|
||||
if (dist == 0) {
|
||||
return Collections.EMPTY_LIST;
|
||||
} else if (dist < 0) {
|
||||
LocalDate x = ed;
|
||||
ed = sd;
|
||||
sd = x;
|
||||
dist = Math.abs(dist);
|
||||
}
|
||||
long max = dist + 1;
|
||||
return Stream.iterate(sd, d -> {
|
||||
return d.plusDays(1);
|
||||
}).limit(max).map(LocalDate::toString).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static class LocalDateAndStr {
|
||||
public LocalDate d;
|
||||
public String s;
|
||||
|
||||
public LocalDateAndStr(LocalDate d, String s) {
|
||||
this.d = d;
|
||||
this.s = s;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<LocalDateAndStr> datesBetween0(String start, String end) {
|
||||
LocalDate sd = LocalDate.parse(start);
|
||||
LocalDate ed = LocalDate.parse(end);
|
||||
long dist = ChronoUnit.DAYS.between(sd, ed);
|
||||
if (dist == 0) {
|
||||
return Collections.EMPTY_LIST;
|
||||
} else if (dist < 0) {
|
||||
LocalDate x = ed;
|
||||
ed = sd;
|
||||
sd = x;
|
||||
dist = Math.abs(dist);
|
||||
}
|
||||
long max = dist + 1;
|
||||
return Stream.iterate(sd, d -> {
|
||||
return d.plusDays(1);
|
||||
}).limit(max).map((LocalDate e) -> {
|
||||
return new LocalDateAndStr(e, e.toString());
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static LocalDate beforeNow(long offsetDays) {
|
||||
return LocalDate.now().minusDays(offsetDays);
|
||||
}
|
||||
|
||||
public static LocalDateTime beforeNowNoTime(long offsetDays) {
|
||||
return LocalDate.now().minusDays(offsetDays).atTime(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
62
src/main/java/com/wehotel/util/DigestUtils.java
Normal file
62
src/main/java/com/wehotel/util/DigestUtils.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.wehotel.util;
|
||||
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public abstract class DigestUtils extends org.apache.commons.codec.digest.DigestUtils {
|
||||
|
||||
static final String md5digest = "$md5digest";
|
||||
|
||||
private DigestUtils() {
|
||||
}
|
||||
|
||||
public static String md532(String source) {
|
||||
byte[] srcBytes = source.getBytes();
|
||||
MessageDigest md = (MessageDigest) ThreadContext.get(md5digest);
|
||||
if (md == null) {
|
||||
md = getMd5Digest();
|
||||
ThreadContext.set(md5digest, md);
|
||||
} else {
|
||||
md.reset();
|
||||
}
|
||||
md.update(srcBytes);
|
||||
byte[] resultBytes = md.digest();
|
||||
return Hex.encodeHexString(resultBytes);
|
||||
}
|
||||
|
||||
public static String md516(String source) {
|
||||
return md532(source).substring(8, 24);
|
||||
}
|
||||
|
||||
// public static void main(String[] args) {
|
||||
// StringBuilder b = new StringBuilder(128);
|
||||
// long now = System.currentTimeMillis();
|
||||
// String app = "appx";
|
||||
// String timestamp = "" + now;
|
||||
// String secretKey = "fb3c057fe6134796acf33d53f240f2a9";
|
||||
// b.append(app).append(Constants.Symbol.UNDERLINE).append(timestamp).append(Constants.Symbol.UNDERLINE).append(secretKey);
|
||||
// System.err.println("timestamp: " + timestamp + ", sign: " + md532(b.toString()));
|
||||
// }
|
||||
}
|
||||
172
src/main/java/com/wehotel/util/JacksonUtils.java
Normal file
172
src/main/java/com/wehotel/util/JacksonUtils.java
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.util;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.*;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.wehotel.plugin.auth.ApiConfig;
|
||||
import com.wehotel.util.Constants.DatetimePattern;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public abstract class JacksonUtils {
|
||||
|
||||
private static ObjectMapper m;
|
||||
|
||||
static {
|
||||
JsonFactory f = new JsonFactory();
|
||||
f.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
|
||||
f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
|
||||
|
||||
m = new ObjectMapper(f);
|
||||
|
||||
m.setSerializationInclusion(Include.NON_EMPTY);
|
||||
m.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
|
||||
m.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
|
||||
m.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
|
||||
m.configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, true); // FIXME
|
||||
m.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, true);
|
||||
m.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
m.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
|
||||
|
||||
SimpleModule m0 = new SimpleModule();
|
||||
m0.addDeserializer(Date.class, new DateDeseralizer());
|
||||
m.registerModule(m0);
|
||||
|
||||
SimpleModule m1 = new SimpleModule();
|
||||
m1.addDeserializer(LocalDate.class, new LocalDateDeseralizer());
|
||||
m.registerModule(m1);
|
||||
|
||||
SimpleModule m2 = new SimpleModule();
|
||||
m2.addDeserializer(LocalDateTime.class, new LocalDateTimeDeseralizer());
|
||||
m.registerModule(m2);
|
||||
|
||||
SimpleModule m3 = new SimpleModule();
|
||||
m3.addSerializer(LocalDateTime.class, new LocalDateTimeSeralizer());
|
||||
m.registerModule(m3);
|
||||
}
|
||||
|
||||
public static ObjectMapper getObjectMapper() {
|
||||
return m;
|
||||
}
|
||||
|
||||
public static <T> T readValue(String json, Class<T> clz) {
|
||||
try {
|
||||
return m.readValue(json, clz);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String writeValueAsString(Object value) {
|
||||
try {
|
||||
return m.writeValueAsString(value);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DateDeseralizer extends JsonDeserializer<Date> {
|
||||
|
||||
public Date deserialize(JsonParser jp, DeserializationContext ctx) throws IOException {
|
||||
|
||||
String s = jp.getText();
|
||||
int sl = s.length();
|
||||
if (sl == DatetimePattern.MILLS_LEN) {
|
||||
return new Date(Long.parseLong(s));
|
||||
} else {
|
||||
String dtp = DatetimePattern.DP10;
|
||||
DateTimeFormatter dtf = null;
|
||||
if (sl == DatetimePattern.DP10.length()) {
|
||||
} else if (sl == DatetimePattern.DP14.length()) {
|
||||
dtp = DatetimePattern.DP14;
|
||||
} else if (sl == DatetimePattern.DP19.length()) {
|
||||
dtp = DatetimePattern.DP19;
|
||||
} else if (sl == DatetimePattern.DP23.length()) {
|
||||
dtp = DatetimePattern.DP23;
|
||||
} else {
|
||||
throw new IOException("invalid datetime pattern: " + s);
|
||||
}
|
||||
dtf = DateTimeUtils.getDateTimeFormatter(dtp);
|
||||
LocalDateTime ldt = LocalDateTime.parse(s, dtf);
|
||||
return DateTimeUtils.from(ldt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LocalDateDeseralizer extends JsonDeserializer<LocalDate> {
|
||||
|
||||
public LocalDate deserialize(JsonParser jp, DeserializationContext ctx) throws IOException {
|
||||
|
||||
String s = jp.getText();
|
||||
if (s.length() == DatetimePattern.DP10.length()) {
|
||||
DateTimeFormatter dtf = DateTimeUtils.getDateTimeFormatter(DatetimePattern.DP10);
|
||||
return LocalDate.parse(s, dtf);
|
||||
} else {
|
||||
throw new IOException("invalid datetime pattern: " + s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LocalDateTimeDeseralizer extends JsonDeserializer<LocalDateTime> {
|
||||
|
||||
public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctx) throws IOException {
|
||||
|
||||
String s = jp.getText();
|
||||
int sl = s.length();
|
||||
if (sl == DatetimePattern.MILLS_LEN) {
|
||||
return DateTimeUtils.from(Long.parseLong(s));
|
||||
} else {
|
||||
String dtp = DatetimePattern.DP10;
|
||||
DateTimeFormatter dtf = null;
|
||||
if (sl == DatetimePattern.DP10.length()) {
|
||||
} else if (sl == DatetimePattern.DP14.length()) {
|
||||
dtp = DatetimePattern.DP14;
|
||||
} else if (sl == DatetimePattern.DP19.length()) {
|
||||
dtp = DatetimePattern.DP19;
|
||||
} else if (sl == DatetimePattern.DP23.length()) {
|
||||
dtp = DatetimePattern.DP23;
|
||||
} else {
|
||||
throw new IOException("invalid datetime pattern: " + s);
|
||||
}
|
||||
dtf = DateTimeUtils.getDateTimeFormatter(dtp);
|
||||
return LocalDateTime.parse(s, dtf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LocalDateTimeSeralizer extends JsonSerializer<LocalDateTime> {
|
||||
|
||||
@Override
|
||||
public void serialize(LocalDateTime ldt, JsonGenerator jg, SerializerProvider sp) throws IOException {
|
||||
jg.writeNumber(DateTimeUtils.toMillis(ldt));
|
||||
}
|
||||
}
|
||||
181
src/main/java/com/wehotel/util/JsonSchemaUtils.java
Normal file
181
src/main/java/com/wehotel/util/JsonSchemaUtils.java
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.util;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.networknt.schema.JsonSchema;
|
||||
import com.networknt.schema.JsonSchemaFactory;
|
||||
import com.networknt.schema.SchemaValidatorsConfig;
|
||||
import com.networknt.schema.SpecVersion;
|
||||
import com.networknt.schema.ValidationMessage;
|
||||
import com.networknt.schema.ValidatorTypeCode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* JSON Schema工具类
|
||||
* @author zhongjie
|
||||
*/
|
||||
public class JsonSchemaUtils {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(JsonSchemaUtils.class);
|
||||
static {
|
||||
try {
|
||||
// 替换验证信息提示
|
||||
Field messageFormatField = ValidatorTypeCode.class.getDeclaredField("messageFormat");
|
||||
//忽略属性的访问权限
|
||||
messageFormatField.setAccessible(true);
|
||||
messageFormatField.set(ValidatorTypeCode.ADDITIONAL_PROPERTIES, new MessageFormat(
|
||||
"{1}在schema中没有定义并且schema不允许指定外的字段"));
|
||||
messageFormatField.set(ValidatorTypeCode.ALL_OF, new MessageFormat("should be valid to all the schemas {1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.ANY_OF, new MessageFormat("should be valid to any of the schemas {1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.CROSS_EDITS, new MessageFormat("has an error with 'cross edits'"));
|
||||
messageFormatField.set(ValidatorTypeCode.DEPENDENCIES, new MessageFormat("has an error with dependencies {1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.EDITS, new MessageFormat("has an error with 'edits'"));
|
||||
messageFormatField.set(ValidatorTypeCode.ENUM, new MessageFormat("值不在限定{1}内"));
|
||||
messageFormatField.set(ValidatorTypeCode.FORMAT, new MessageFormat("不符合{1}格式{2}"));
|
||||
messageFormatField.set(ValidatorTypeCode.ITEMS, new MessageFormat("在索引[{1}]处为找到验证器"));
|
||||
messageFormatField.set(ValidatorTypeCode.MAXIMUM, new MessageFormat("给定值应当小于等于{1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.MAX_ITEMS, new MessageFormat("数组至多含有{1}个元素"));
|
||||
messageFormatField.set(ValidatorTypeCode.MAX_LENGTH, new MessageFormat("长度应当最多{1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.MAX_PROPERTIES, new MessageFormat("对象最多有{1}个字段"));
|
||||
messageFormatField.set(ValidatorTypeCode.MINIMUM, new MessageFormat("给定值应当大于等于{1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.MIN_ITEMS, new MessageFormat("数组至少含有{1}个元素"));
|
||||
messageFormatField.set(ValidatorTypeCode.MIN_LENGTH, new MessageFormat("长度应当最少{1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.MIN_PROPERTIES, new MessageFormat("{0}:对象最少有{1}个字段"));
|
||||
messageFormatField.set(ValidatorTypeCode.MULTIPLE_OF, new MessageFormat("数值类型应当是{1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.NOT_ALLOWED, new MessageFormat("{1}不允许出现在数据中"));
|
||||
messageFormatField.set(ValidatorTypeCode.NOT, new MessageFormat("should not be valid to the schema {1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.ONE_OF, new MessageFormat("should be valid to one and only one of the schemas {1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.PATTERN_PROPERTIES, new MessageFormat("has some error with 'pattern properties'"));
|
||||
messageFormatField.set(ValidatorTypeCode.PATTERN, new MessageFormat("应当符合格式\"{1}\""));
|
||||
messageFormatField.set(ValidatorTypeCode.PROPERTIES, new MessageFormat("对象字段存在错误"));
|
||||
messageFormatField.set(ValidatorTypeCode.READ_ONLY, new MessageFormat("is a readonly field, it cannot be changed"));
|
||||
messageFormatField.set(ValidatorTypeCode.REF, new MessageFormat("has an error with 'refs'"));
|
||||
messageFormatField.set(ValidatorTypeCode.REQUIRED, new MessageFormat("{1}字段不能为空"));
|
||||
messageFormatField.set(ValidatorTypeCode.TYPE, new MessageFormat("预期类型是{2},但实际是{1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.UNION_TYPE, new MessageFormat("预期类型是{2},但实际是{1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.UNIQUE_ITEMS, new MessageFormat("数组元素唯一性冲突"));
|
||||
messageFormatField.set(ValidatorTypeCode.DATETIME, new MessageFormat("{1}不是一个有效的{2}"));
|
||||
messageFormatField.set(ValidatorTypeCode.UUID, new MessageFormat("{1}不是一个有效的{2}"));
|
||||
messageFormatField.set(ValidatorTypeCode.ID, new MessageFormat("{1} is an invalid segment for URI {2}"));
|
||||
messageFormatField.set(ValidatorTypeCode.EXCLUSIVE_MAXIMUM, new MessageFormat("给定值应当小于{1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.EXCLUSIVE_MINIMUM, new MessageFormat("给定值应当大于{1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.FALSE, new MessageFormat("Boolean schema false is not valid"));
|
||||
messageFormatField.set(ValidatorTypeCode.CONST, new MessageFormat("值应当是一个常量{1}"));
|
||||
messageFormatField.set(ValidatorTypeCode.CONTAINS, new MessageFormat("没有包含元素能够通过验证:{1}"));
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("替换ValidatorTypeCode.messageFormat异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
private JsonSchemaUtils() {}
|
||||
|
||||
/**
|
||||
* 验证JSON字符串是否符合JSON Schema要求
|
||||
* @param jsonSchema JSON Schema
|
||||
* @param inputJson JSON字符串
|
||||
* @return null:验证通过,List:报错信息列表
|
||||
*/
|
||||
public static List<String> validate(String jsonSchema, String inputJson) {
|
||||
return internalValidate(jsonSchema, inputJson, Boolean.FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证JSON字符串是否符合JSON Schema要求,允许数字\布尔类型 是字符串格式
|
||||
* @param jsonSchema JSON Schema
|
||||
* @param inputJson JSON字符串
|
||||
* @return null:验证通过,List:报错信息列表
|
||||
*/
|
||||
public static List<String> validateAllowValueStr(String jsonSchema, String inputJson) {
|
||||
return internalValidate(jsonSchema, inputJson, Boolean.TRUE);
|
||||
}
|
||||
|
||||
private static List<String> internalValidate(String jsonSchema, String inputJson, boolean typeLoose) {
|
||||
CheckJsonResult checkJsonResult = checkJson(jsonSchema, inputJson, typeLoose);
|
||||
if (checkJsonResult.errorList != null) {
|
||||
return checkJsonResult.errorList;
|
||||
}
|
||||
|
||||
Set<ValidationMessage> validationMessageSet = checkJsonResult.schema.validate(checkJsonResult.json);
|
||||
if (CollectionUtils.isEmpty(validationMessageSet)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return validationMessageSet.stream().map(validationMessage -> {
|
||||
String message = validationMessage.getMessage();
|
||||
if (message != null) {
|
||||
return message;
|
||||
}
|
||||
return validationMessage.getCode();
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static CheckJsonResult checkJson(String jsonSchema, String inputJson, boolean typeLoose) {
|
||||
CheckJsonResult checkJsonResult = new CheckJsonResult();
|
||||
try {
|
||||
checkJsonResult.schema = getJsonSchemaFromStringContent(jsonSchema, typeLoose);
|
||||
} catch (Exception e) {
|
||||
checkJsonResult.errorList = new ArrayList<>(1);
|
||||
checkJsonResult.errorList.add(String.format("JSON Schema格式错误,提示信息[%s]", e.getMessage()));
|
||||
return checkJsonResult;
|
||||
}
|
||||
|
||||
try {
|
||||
checkJsonResult.json = getJsonNodeFromStringContent(inputJson);
|
||||
} catch (Exception e) {
|
||||
checkJsonResult.errorList = new ArrayList<>(1);
|
||||
checkJsonResult.errorList.add(String.format("待验证JSON格式错误,提示信息[%s]", e.getMessage()));
|
||||
return checkJsonResult;
|
||||
}
|
||||
|
||||
return checkJsonResult;
|
||||
}
|
||||
|
||||
private static class CheckJsonResult {
|
||||
JsonSchema schema;
|
||||
JsonNode json;
|
||||
List<String> errorList;
|
||||
}
|
||||
|
||||
private static final JsonSchemaFactory JSON_SCHEMA_FACTORY = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
|
||||
private static final SchemaValidatorsConfig CONFIG_WITH_TYPE_LOOSE;
|
||||
private static final SchemaValidatorsConfig CONFIG_WITHOUT_TYPE_LOOSE;
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
static {
|
||||
CONFIG_WITH_TYPE_LOOSE = new SchemaValidatorsConfig();
|
||||
CONFIG_WITH_TYPE_LOOSE.setTypeLoose(Boolean.TRUE);
|
||||
CONFIG_WITHOUT_TYPE_LOOSE = new SchemaValidatorsConfig();
|
||||
CONFIG_WITHOUT_TYPE_LOOSE.setTypeLoose(Boolean.FALSE);
|
||||
}
|
||||
private static JsonSchema getJsonSchemaFromStringContent(String schemaContent, boolean typeLoose) {
|
||||
SchemaValidatorsConfig config = typeLoose ? CONFIG_WITH_TYPE_LOOSE : CONFIG_WITHOUT_TYPE_LOOSE;
|
||||
return JSON_SCHEMA_FACTORY.getSchema(schemaContent, config);
|
||||
}
|
||||
|
||||
private static JsonNode getJsonNodeFromStringContent(String content) throws Exception {
|
||||
return OBJECT_MAPPER.readTree(content);
|
||||
}
|
||||
}
|
||||
179
src/main/java/com/wehotel/util/MapUtil.java
Normal file
179
src/main/java/com/wehotel/util/MapUtil.java
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author francis
|
||||
*
|
||||
*/
|
||||
public class MapUtil {
|
||||
|
||||
public static HttpHeaders toHttpHeaders(Map<String, Object> params) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
if (params.isEmpty()) {
|
||||
return headers;
|
||||
}
|
||||
|
||||
for (Entry<String, Object> entry : params.entrySet()) {
|
||||
Object val = entry.getValue();
|
||||
List<String> list = new ArrayList<>();
|
||||
if (val instanceof List) {
|
||||
List<Object> vals = (List<Object>) val;
|
||||
for (Object value : vals) {
|
||||
if (value != null) {
|
||||
list.add(value.toString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (val != null) {
|
||||
list.add(val.toString());
|
||||
}
|
||||
}
|
||||
if (list.size() > 0) {
|
||||
headers.put(entry.getKey(), list);
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
public static MultiValueMap<String, String> toMultiValueMap(Map<String, Object> params) {
|
||||
MultiValueMap<String, String> mvmap = new LinkedMultiValueMap<>();
|
||||
|
||||
if (params.isEmpty()) {
|
||||
return mvmap;
|
||||
}
|
||||
|
||||
for (Entry<String, Object> entry : params.entrySet()) {
|
||||
Object val = entry.getValue();
|
||||
List<String> list = new ArrayList<>();
|
||||
if (val instanceof List) {
|
||||
List<Object> vals = (List<Object>) val;
|
||||
for (Object value : vals) {
|
||||
if (value != null) {
|
||||
list.add(value.toString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (val != null) {
|
||||
list.add(val.toString());
|
||||
}
|
||||
}
|
||||
if (list.size() > 0) {
|
||||
mvmap.put(entry.getKey(), list);
|
||||
}
|
||||
}
|
||||
|
||||
return mvmap;
|
||||
}
|
||||
|
||||
public static Map<String, Object> toHashMap(MultiValueMap<String, String> params) {
|
||||
HashMap<String, Object> m = new HashMap<>();
|
||||
|
||||
if (params.isEmpty()) {
|
||||
return m;
|
||||
}
|
||||
|
||||
for (Entry<String, List<String>> entry : params.entrySet()) {
|
||||
List<String> val = entry.getValue();
|
||||
if (val != null && val.size() > 0) {
|
||||
if (val.size() > 1) {
|
||||
m.put(entry.getKey(), val);
|
||||
} else {
|
||||
m.put(entry.getKey(), val.get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value by path,support multiple levels,eg:a.b.c <br>
|
||||
* Do NOT use this method if field name contains a dot <br>
|
||||
* @param data
|
||||
* @param path
|
||||
* @param value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void set(Map<String, Object> data, String path, Object value) {
|
||||
String[] fields = path.split("\\.");
|
||||
if(fields.length < 2) {
|
||||
data.put(path, value);
|
||||
}else {
|
||||
Map<String, Object> next = data;
|
||||
for (int i = 0; i < fields.length - 1; i++) {
|
||||
Map<String, Object> val = (Map<String, Object>) next.get(fields[i]);
|
||||
if(val == null) {
|
||||
val = new HashMap<>();
|
||||
next.put(fields[i], val);
|
||||
}
|
||||
if(i == fields.length - 2) {
|
||||
val.put(fields[i+1], value);
|
||||
break;
|
||||
}
|
||||
next = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value by path, support multiple levels,eg:a.b.c <br>
|
||||
* Do NOT use this method if field name contains a dot <br>
|
||||
* @param data
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public static Object get(Map<String, Object> data, String path) {
|
||||
String[] fields = path.split("\\.");
|
||||
if(fields.length < 2) {
|
||||
return data.get(path);
|
||||
}else {
|
||||
Map<String, Object> next = data;
|
||||
for (int i = 0; i < fields.length - 1; i++) {
|
||||
if(!(next.get(fields[i]) instanceof Map)) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> val = (Map<String, Object>) next.get(fields[i]);
|
||||
if(val == null) {
|
||||
return null;
|
||||
}
|
||||
if(i == fields.length - 2) {
|
||||
return val.get(fields[i+1]);
|
||||
}
|
||||
next = val;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
99
src/main/java/com/wehotel/util/NetworkUtils.java
Normal file
99
src/main/java/com/wehotel/util/NetworkUtils.java
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public class NetworkUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(NetworkUtils.class);
|
||||
|
||||
private static final int maxServerId = 1023;
|
||||
|
||||
private static int serverId = -1;
|
||||
|
||||
private static String serverIp;
|
||||
|
||||
public static String getServerIp() {
|
||||
try {
|
||||
if (serverIp == null) {
|
||||
boolean found = false;
|
||||
Enumeration<NetworkInterface> nis = null;
|
||||
nis = NetworkInterface.getNetworkInterfaces();
|
||||
while (nis.hasMoreElements()) {
|
||||
NetworkInterface ni = (NetworkInterface) nis.nextElement();
|
||||
Enumeration<InetAddress> ias = ni.getInetAddresses();
|
||||
while (ias.hasMoreElements()) {
|
||||
InetAddress ia = ias.nextElement();
|
||||
if (ia.isSiteLocalAddress()) {
|
||||
serverIp = ia.getHostAddress();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
InetAddress ia = InetAddress.getLocalHost();
|
||||
serverIp = ia.getHostAddress();
|
||||
}
|
||||
}
|
||||
return serverIp;
|
||||
} catch (SocketException | UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getServerId() {
|
||||
if (serverId == -1) {
|
||||
try {
|
||||
StringBuilder b = new StringBuilder();
|
||||
Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
|
||||
while (nis.hasMoreElements()) {
|
||||
NetworkInterface ni = nis.nextElement();
|
||||
byte[] mac = ni.getHardwareAddress();
|
||||
if (mac != null) {
|
||||
for (int i = 0; i < mac.length; i++) {
|
||||
b.append(String.format("%02X", mac[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
serverId = b.toString().hashCode();
|
||||
} catch (Exception e) {
|
||||
serverId = (new SecureRandom().nextInt());
|
||||
log.error(null, e);
|
||||
}
|
||||
serverId = serverId & maxServerId;
|
||||
log.info("server id is " + serverId);
|
||||
}
|
||||
return serverId;
|
||||
}
|
||||
}
|
||||
36
src/main/java/com/wehotel/util/ReactorUtils.java
Normal file
36
src/main/java/com/wehotel/util/ReactorUtils.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.util;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public interface ReactorUtils {
|
||||
|
||||
static final Object OBJ = new Object();
|
||||
|
||||
static final Object NULL = OBJ;
|
||||
|
||||
static final Mono<Object> INITIATE = Mono.just(NULL);
|
||||
|
||||
static final Mono<Object> EMPTY_ASYNC_TASK = INITIATE;
|
||||
|
||||
static final Throwable EMPTY_THROWABLE = new Throwable(null, null, false, false) {};
|
||||
}
|
||||
44
src/main/java/com/wehotel/util/Script.java
Normal file
44
src/main/java/com/wehotel/util/Script.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.util;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public class Script {
|
||||
|
||||
private String type = ScriptUtils.GROOVY;
|
||||
|
||||
private String source;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
}
|
||||
131
src/main/java/com/wehotel/util/ScriptUtils.java
Normal file
131
src/main/java/com/wehotel/util/ScriptUtils.java
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.util;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.script.*;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public abstract class ScriptUtils {
|
||||
|
||||
public static final String JAVA_SCRIPT = "javascript";
|
||||
|
||||
public static final String GROOVY = "groovy";
|
||||
|
||||
private static ScriptEngineManager engineManger;
|
||||
|
||||
private static final String jsFuncName = "dyFunc";
|
||||
|
||||
private static final String clazz = "clazz";
|
||||
|
||||
private static final String resJsonStr = "resJsonStr";
|
||||
|
||||
public static Map<Long, Long> recreateJavascriptEngineSignalMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
engineManger = new ScriptEngineManager();
|
||||
}
|
||||
|
||||
private static ScriptEngine createJavascriptEngine() throws ScriptException {
|
||||
ScriptEngine eng = engineManger.getEngineByName(JAVA_SCRIPT);
|
||||
try {
|
||||
eng.eval(new FileReader("js/common.js"));
|
||||
return eng;
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ScriptException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ScriptEngine getScriptEngine(String type) throws ScriptException {
|
||||
if (GROOVY.equals(type)) {
|
||||
ScriptEngine groovyEngine = (ScriptEngine) ThreadContext.get(GROOVY);
|
||||
if (groovyEngine == null) {
|
||||
groovyEngine = engineManger.getEngineByName(GROOVY);
|
||||
ThreadContext.set(GROOVY, groovyEngine);
|
||||
}
|
||||
return groovyEngine;
|
||||
|
||||
} else if (JAVA_SCRIPT.equals(type)) {
|
||||
ScriptEngine javascriptEngine;
|
||||
long tid = Thread.currentThread().getId();
|
||||
Object signal = recreateJavascriptEngineSignalMap.get(tid);
|
||||
if (signal == null) {
|
||||
javascriptEngine = createJavascriptEngine();
|
||||
recreateJavascriptEngineSignalMap.put(tid, tid);
|
||||
ThreadContext.set(JAVA_SCRIPT, javascriptEngine);
|
||||
} else {
|
||||
javascriptEngine = (ScriptEngine) ThreadContext.get(JAVA_SCRIPT);
|
||||
}
|
||||
return javascriptEngine;
|
||||
|
||||
} else {
|
||||
throw new ScriptException("unknown script engine type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
public static Object execute(Script script) throws ScriptException {
|
||||
return execute(script, null);
|
||||
}
|
||||
|
||||
public static Object execute(Script script, Map<String, Object> context) throws ScriptException {
|
||||
String type = script.getType();
|
||||
ScriptEngine engine = getScriptEngine(type);
|
||||
String src = script.getSource();
|
||||
if (GROOVY.equals(type)) {
|
||||
if (context == null) {
|
||||
return engine.eval(src);
|
||||
} else {
|
||||
Bindings bis = engine.createBindings();
|
||||
bis.putAll(context);
|
||||
return engine.eval(src, bis);
|
||||
}
|
||||
} else { // js
|
||||
engine.eval(src);
|
||||
Invocable invocable = (Invocable) engine;
|
||||
String paramsJsonStr = StringUtils.EMPTY;
|
||||
// try {
|
||||
// ObjectMapper mapper = JacksonUtils.getObjectMapper();
|
||||
// if (context != null) {
|
||||
// paramsJsonStr = mapper.writeValueAsString(context);
|
||||
// }
|
||||
// ScriptObjectMirror som = (ScriptObjectMirror) invocable.invokeFunction(jsFuncName, paramsJsonStr);
|
||||
// Class<?> clz = Class.forName(som.get(clazz).toString());
|
||||
// return mapper.readValue(som.get(resJsonStr).toString(), clz);
|
||||
// } catch (JsonProcessingException | NoSuchMethodException | ClassNotFoundException e) {
|
||||
// throw new ScriptException(e);
|
||||
// }
|
||||
try {
|
||||
// ObjectMapper mapper = JacksonUtils.getObjectMapper();
|
||||
if (context != null) {
|
||||
paramsJsonStr = JacksonUtils.writeValueAsString(context);
|
||||
}
|
||||
return invocable.invokeFunction(jsFuncName, paramsJsonStr);
|
||||
} catch (NoSuchMethodException | RuntimeException e) {
|
||||
throw new ScriptException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
115
src/main/java/com/wehotel/util/ThreadContext.java
Normal file
115
src/main/java/com/wehotel/util/ThreadContext.java
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.util;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public abstract class ThreadContext {
|
||||
|
||||
private static ThreadLocal<Map<String, Object>> tl = new ThreadLocal<>();
|
||||
private static final int mapCap = 32;
|
||||
|
||||
private static final String sb = "_sb";
|
||||
private static final int sbCap = 256;
|
||||
|
||||
/** use me carefully! */
|
||||
public static StringBuilder getStringBuilder() {
|
||||
return getStringBuilder(true);
|
||||
}
|
||||
|
||||
/** use me carefully! */
|
||||
public static StringBuilder getStringBuilder(boolean clean) {
|
||||
// Map<String, Object> m = getMap();
|
||||
// StringBuilder b = (StringBuilder) m.get(sb);
|
||||
// if (b == null) {
|
||||
// b = new StringBuilder(sbCap);
|
||||
// m.put(sb, b);
|
||||
// } else {
|
||||
// if (clean) {
|
||||
// b.delete(0, b.length());
|
||||
// }
|
||||
// }
|
||||
// return b;
|
||||
return new StringBuilder(64);
|
||||
}
|
||||
|
||||
public static StringBuilder getStringBuilder(String key) {
|
||||
// StringBuilder b = (StringBuilder) get(key);
|
||||
// if (b == null) {
|
||||
// b = new StringBuilder(sbCap);
|
||||
// Map<String, Object> m = getMap();
|
||||
// m.put(key, b);
|
||||
// } else {
|
||||
// b.delete(0, b.length());
|
||||
// }
|
||||
// return b;
|
||||
return getStringBuilder(true);
|
||||
}
|
||||
|
||||
/** for legacy code. */
|
||||
public static SimpleDateFormat getSimpleDateFormat(String pattern) {
|
||||
Map<String, Object> m = getMap();
|
||||
SimpleDateFormat sdf = (SimpleDateFormat) m.get(pattern);
|
||||
if (sdf == null) {
|
||||
sdf = new SimpleDateFormat(pattern);
|
||||
m.put(pattern, sdf);
|
||||
}
|
||||
return sdf;
|
||||
}
|
||||
|
||||
private static Map<String, Object> getMap() {
|
||||
Map<String, Object> m = tl.get();
|
||||
if (m == null) {
|
||||
m = new HashMap<>(mapCap);
|
||||
tl.set(m);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Object get(String key) {
|
||||
return getMap().get(key);
|
||||
}
|
||||
|
||||
public static <T> T get(String key, Class<T> clz) {
|
||||
T t = (T) get(key);
|
||||
if (t == null) {
|
||||
try {
|
||||
t = clz.newInstance();
|
||||
set(key, t);
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
public static void set(String key, Object obj) {
|
||||
getMap().put(key, obj);
|
||||
}
|
||||
|
||||
public static Object remove(String key) {
|
||||
return getMap().remove(key);
|
||||
}
|
||||
}
|
||||
85
src/main/java/com/wehotel/util/Utils.java
Normal file
85
src/main/java/com/wehotel/util/Utils.java
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.util;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public abstract class Utils {
|
||||
|
||||
public static void addTo(StringBuilder b, String k, char c, boolean v, String separator) {
|
||||
b.append(k).append(c).append(v).append(separator);
|
||||
}
|
||||
|
||||
public static void addTo(StringBuilder b, String k, char c, char v, String separator) {
|
||||
b.append(k).append(c).append(v).append(separator);
|
||||
}
|
||||
|
||||
public static void addTo(StringBuilder b, String k, char c, int v, String separator) {
|
||||
b.append(k).append(c).append(v).append(separator);
|
||||
}
|
||||
|
||||
public static void addTo(StringBuilder b, String k, char c, long v, String separator) {
|
||||
b.append(k).append(c).append(v).append(separator);
|
||||
}
|
||||
|
||||
public static void addTo(StringBuilder b, String k, char c, float v, String separator) {
|
||||
b.append(k).append(c).append(v).append(separator);
|
||||
}
|
||||
|
||||
public static void addTo(StringBuilder b, String k, char c, double v, String separator) {
|
||||
b.append(k).append(c).append(v).append(separator);
|
||||
}
|
||||
|
||||
public static void addTo(StringBuilder b, String k, char c, String v, String separator) {
|
||||
b.append(k).append(c).append(v).append(separator);
|
||||
}
|
||||
|
||||
public static void addTo(StringBuilder b, String k, char c, LocalTime v, String separator) {
|
||||
b.append(k).append(c).append(v).append(separator);
|
||||
}
|
||||
|
||||
public static void addTo(StringBuilder b, String k, char c, LocalDate v, String separator) {
|
||||
b.append(k).append(c).append(v).append(separator);
|
||||
}
|
||||
|
||||
public static void addTo(StringBuilder b, String k, char c, LocalDateTime v, String separator) { b.append(k).append(c).append(v).append(separator); }
|
||||
|
||||
public static void addTo(StringBuilder b, String k, char c, Object v, String separator) {
|
||||
b.append(k).append(c).append(v).append(separator);
|
||||
}
|
||||
|
||||
public static String initials2lowerCase(String s) {
|
||||
if (StringUtils.isBlank(s)) {
|
||||
return s;
|
||||
}
|
||||
int cp = s.codePointAt(0);
|
||||
if (cp < 65 || cp > 90) {
|
||||
return s;
|
||||
}
|
||||
char[] ca = s.toCharArray();
|
||||
ca[0] += 32;
|
||||
return String.valueOf(ca);
|
||||
}
|
||||
}
|
||||
373
src/main/java/com/wehotel/util/WebUtils.java
Normal file
373
src/main/java/com/wehotel/util/WebUtils.java
Normal file
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.wehotel.util;
|
||||
|
||||
import com.wehotel.flume.clients.log4j2appender.LogService;
|
||||
import com.wehotel.filter.FilterResult;
|
||||
import com.wehotel.legacy.RespEntity;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author lancer
|
||||
*/
|
||||
|
||||
public abstract class WebUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(WebUtils.class);
|
||||
|
||||
public static final String APP_HEADER = "fizz-appid";
|
||||
|
||||
private static final String directResponse = "directResponse";
|
||||
|
||||
public static final String FILTER_CONTEXT = "filterContext";
|
||||
|
||||
public static final String APPEND_HEADERS = "appendHeaders";
|
||||
|
||||
public static final String PREV_FILTER_RESULT = "prevFilterResult";
|
||||
|
||||
private static final String request_path = "reqPath";
|
||||
|
||||
private static final String SERVICE_ID = "serviceId";
|
||||
|
||||
private static final String xForwardedFor = "X_FORWARDED_FOR";
|
||||
|
||||
private static final String unknown = "unknown";
|
||||
|
||||
private static final String loopBack = "127.0.0.1";
|
||||
|
||||
private static final String binaryAddress = "0:0:0:0:0:0:0:1";
|
||||
|
||||
public static boolean logResponseBody = false;
|
||||
|
||||
public static Set<String> logHeaderSet = Collections.EMPTY_SET;
|
||||
|
||||
private static final String response = " response ";
|
||||
|
||||
private static final String originIp = "originIp";
|
||||
|
||||
public static final String CGG = "cgg";
|
||||
|
||||
public static String getHeaderValue(ServerWebExchange exchange, String header) {
|
||||
return exchange.getRequest().getHeaders().getFirst(header);
|
||||
}
|
||||
|
||||
public static List<String> getHeaderValues(ServerWebExchange exchange, String header) {
|
||||
return exchange.getRequest().getHeaders().get(header);
|
||||
}
|
||||
|
||||
public static String getAppId(ServerWebExchange exchange) {
|
||||
return exchange.getAttribute(APP_HEADER);
|
||||
}
|
||||
|
||||
public static Character getCurrentGatewayGroup(ServerWebExchange exchange) {
|
||||
return exchange.getAttribute(CGG);
|
||||
}
|
||||
|
||||
public static String getServiceId(ServerWebExchange exchange) {
|
||||
String svc = exchange.getAttribute(SERVICE_ID);
|
||||
if (svc == null) {
|
||||
String p = exchange.getRequest().getPath().value();
|
||||
int pl = p.length();
|
||||
if (pl < 15) {
|
||||
} else {
|
||||
boolean b = false;
|
||||
if (p.charAt(2) == 'r' && p.charAt(3) == 'o' && p.charAt(4) == 'x') {
|
||||
b = true;
|
||||
}
|
||||
if (b) {
|
||||
byte i = 9;
|
||||
if (p.charAt(6) == 't') {
|
||||
i = 13;
|
||||
}
|
||||
for (; i < pl; i++) {
|
||||
if (p.charAt(i) == Constants.Symbol.FORWARD_SLASH) {
|
||||
if (p.charAt(6) == 't') {
|
||||
svc = p.substring(11, i);
|
||||
} else {
|
||||
svc = p.substring(7, i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
exchange.getAttributes().put(SERVICE_ID, svc);
|
||||
}
|
||||
}
|
||||
}
|
||||
return svc;
|
||||
}
|
||||
|
||||
public static Mono<Void> getDirectResponse(ServerWebExchange exchange) {
|
||||
return (Mono<Void>) exchange.getAttributes().get(WebUtils.directResponse);
|
||||
}
|
||||
|
||||
public static Map<String, FilterResult> getFilterContext(ServerWebExchange exchange) {
|
||||
return (Map<String, FilterResult>) exchange.getAttributes().get(FILTER_CONTEXT);
|
||||
}
|
||||
|
||||
public static FilterResult getFilterResult(ServerWebExchange exchange, String filter) {
|
||||
return getFilterContext(exchange).get(filter);
|
||||
}
|
||||
|
||||
public static Map<String, Object> getFilterResultData(ServerWebExchange exchange, String filter) {
|
||||
return getFilterResult(exchange, filter).data;
|
||||
}
|
||||
|
||||
public static Object getFilterResultDataItem(ServerWebExchange exchange, String filter, String key) {
|
||||
return getFilterResultData(exchange, filter).get(key);
|
||||
}
|
||||
|
||||
public static Mono<Void> buildDirectResponse(ServerWebExchange exchange, HttpStatus status, HttpHeaders headers, String bodyContent) {
|
||||
return buildDirectResponse(exchange.getResponse(), status, headers, bodyContent);
|
||||
}
|
||||
|
||||
public static Mono buildDirectResponseAndBindContext(ServerWebExchange exchange, HttpStatus status, HttpHeaders headers, String bodyContent) {
|
||||
Mono<Void> mv = buildDirectResponse(exchange, status, headers, bodyContent);
|
||||
exchange.getAttributes().put(WebUtils.directResponse, mv);
|
||||
return mv;
|
||||
}
|
||||
|
||||
public static Mono buildJsonDirectResponse(ServerWebExchange exchange, HttpStatus status, @Nullable HttpHeaders headers, String json) {
|
||||
if (headers == null) {
|
||||
headers = new HttpHeaders();
|
||||
}
|
||||
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
return buildDirectResponse(exchange, status, headers, json);
|
||||
}
|
||||
|
||||
public static Mono buildJsonDirectResponseAndBindContext(ServerWebExchange exchange, HttpStatus status, @Nullable HttpHeaders headers, String json) {
|
||||
if (headers == null) {
|
||||
headers = new HttpHeaders();
|
||||
}
|
||||
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
return buildDirectResponseAndBindContext(exchange, status, headers, json);
|
||||
}
|
||||
|
||||
public static Mono<Void> buildDirectResponse(ServerHttpResponse clientResp, HttpStatus status, HttpHeaders headers, String bodyContent) {
|
||||
if (clientResp.isCommitted()) {
|
||||
log.warn(bodyContent + ", but client resp is committed, " + clientResp.getStatusCode());
|
||||
return Mono.error(new RuntimeException(bodyContent, null, false, false) {});
|
||||
}
|
||||
clientResp.setStatusCode(status);
|
||||
headers.forEach(
|
||||
(h, vs) -> {
|
||||
clientResp.getHeaders().addAll(h, vs);
|
||||
}
|
||||
);
|
||||
return clientResp
|
||||
.writeWith(Mono.just(clientResp.bufferFactory().wrap(bodyContent.getBytes())));
|
||||
}
|
||||
|
||||
public static void transmitSuccessFilterResult(ServerWebExchange exchange, String filter, Map<String, Object> data) {
|
||||
FilterResult fr = FilterResult.SUCCESS_WITH(filter, data);
|
||||
bind(exchange, filter, fr);
|
||||
}
|
||||
|
||||
public static Mono transmitSuccessFilterResultAndEmptyMono(ServerWebExchange exchange, String filter, Map<String, Object> data) {
|
||||
transmitSuccessFilterResult(exchange, filter, data);
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
public static void transmitFailFilterResult(ServerWebExchange exchange, String filter) {
|
||||
FilterResult fr = FilterResult.FAIL(filter);
|
||||
bind(exchange, filter, fr);
|
||||
}
|
||||
|
||||
public static void transmitFailFilterResult(ServerWebExchange exchange, String filter, Throwable cause) {
|
||||
FilterResult fr = FilterResult.FAIL_WITH(filter, cause);
|
||||
bind(exchange, filter, fr);
|
||||
}
|
||||
|
||||
private static void bind(ServerWebExchange exchange, String filter, FilterResult fr) {
|
||||
Map<String, FilterResult> fc = getFilterContext(exchange);
|
||||
fc.put(filter, fr);
|
||||
fc.put(PREV_FILTER_RESULT, fr);
|
||||
}
|
||||
|
||||
public static FilterResult getPrevFilterResult(ServerWebExchange exchange) {
|
||||
return getFilterContext(exchange).get(PREV_FILTER_RESULT);
|
||||
}
|
||||
|
||||
public static String getReqPath(ServerWebExchange exchange) {
|
||||
String path = exchange.getAttribute(request_path);
|
||||
if (path == null) {
|
||||
path = exchange.getRequest().getPath().value();
|
||||
path = path.substring(path.indexOf(Constants.Symbol.FORWARD_SLASH, 11), path.length());
|
||||
exchange.getAttributes().put(request_path, path);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static String getRelativeUri(ServerWebExchange exchange) {
|
||||
String relativeUri = getReqPath(exchange);
|
||||
String qry = exchange.getRequest().getURI().getQuery();
|
||||
if (qry != null) {
|
||||
if (StringUtils.indexOfAny(qry, Constants.Symbol.LEFT_BRACE, Constants.Symbol.FORWARD_SLASH, Constants.Symbol.HASH) > 0) {
|
||||
qry = exchange.getRequest().getURI().getRawQuery();
|
||||
}
|
||||
relativeUri = relativeUri + Constants.Symbol.QUESTION + qry;
|
||||
}
|
||||
return relativeUri;
|
||||
}
|
||||
|
||||
public static Map<String, String> getAppendHeaders(ServerWebExchange exchange) {
|
||||
return (Map<String, String>) exchange.getAttributes().get(APPEND_HEADERS);
|
||||
}
|
||||
|
||||
public static Map<String, String> appendHeader(ServerWebExchange exchange, String name, String value) {
|
||||
Map<String, String> hdrs = getAppendHeaders(exchange);
|
||||
hdrs.put(name, value);
|
||||
return hdrs;
|
||||
}
|
||||
|
||||
public static void request2stringBuilder(ServerWebExchange exchange, StringBuilder b) {
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
request2stringBuilder(req.getId(), req.getMethod(), req.getURI().toString(), req.getHeaders(), null, b);
|
||||
}
|
||||
|
||||
public static void request2stringBuilder(String reqId, HttpMethod method, String uri, HttpHeaders headers, Object body, StringBuilder b) {
|
||||
b.append(reqId).append(Constants.Symbol.SPACE).append(method).append(Constants.Symbol.SPACE).append(uri);
|
||||
if (headers != null) {
|
||||
final boolean[] f = {false};
|
||||
logHeaderSet.forEach(
|
||||
h -> {
|
||||
String v = headers.getFirst(h);
|
||||
if (v != null) {
|
||||
if (!f[0]) {
|
||||
b.append(Constants.Symbol.LINE_SEPARATOR);
|
||||
f[0] = true;
|
||||
}
|
||||
Utils.addTo(b, h, Constants.Symbol.EQUAL, v, Constants.Symbol.TWO_SPACE_STR);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
// body to b
|
||||
}
|
||||
|
||||
public static void response2stringBuilder(String rid, ClientResponse clientResponse, StringBuilder b) {
|
||||
b.append(rid).append(response).append(clientResponse.statusCode());
|
||||
HttpHeaders headers = clientResponse.headers().asHttpHeaders();
|
||||
final boolean[] f = {false};
|
||||
logHeaderSet.forEach(
|
||||
h -> {
|
||||
String v = headers.getFirst(h);
|
||||
if (v != null) {
|
||||
if (!f[0]) {
|
||||
b.append(Constants.Symbol.LINE_SEPARATOR);
|
||||
f[0] = true;
|
||||
}
|
||||
Utils.addTo(b, h, Constants.Symbol.EQUAL, v, Constants.Symbol.TWO_SPACE_STR);
|
||||
}
|
||||
}
|
||||
);
|
||||
// body to b
|
||||
}
|
||||
|
||||
private static Mono<Void> responseError(ServerWebExchange exchange, String filter, int code, String msg, Throwable t, boolean bindContext) {
|
||||
// Mono<DataBuffer> reqBodyMono = getRequestBody(exchange);
|
||||
// final DataBuffer[] reqBody = {null};
|
||||
// if (reqBodyMono != null) {
|
||||
// reqBodyMono.subscribe(
|
||||
// db -> {
|
||||
// reqBody[0] = db;
|
||||
// DataBufferUtils.retain(reqBody[0]);
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
String rid = exchange.getRequest().getId();
|
||||
// Schedulers.parallel().schedule(() -> {
|
||||
StringBuilder b = ThreadContext.getStringBuilder();
|
||||
request2stringBuilder(exchange, b);
|
||||
// if (reqBody[0] != null) {
|
||||
// DataBufferUtils.release(reqBody[0]);
|
||||
// }
|
||||
b.append(Constants.Symbol.LINE_SEPARATOR);
|
||||
b.append(filter).append(Constants.Symbol.SPACE).append(code).append(Constants.Symbol.SPACE).append(msg);
|
||||
if (t == null) {
|
||||
log.error(b.toString(), LogService.BIZ_ID, rid);
|
||||
} else {
|
||||
log.error(b.toString(), LogService.BIZ_ID, rid, t);
|
||||
Throwable[] suppressed = t.getSuppressed();
|
||||
if (suppressed != null && suppressed.length != 0) {
|
||||
log.error(StringUtils.EMPTY, suppressed[0]);
|
||||
}
|
||||
}
|
||||
// });
|
||||
if (filter != null) {
|
||||
if (t == null) {
|
||||
transmitFailFilterResult(exchange, filter);
|
||||
} else {
|
||||
transmitFailFilterResult(exchange, filter, t);
|
||||
}
|
||||
}
|
||||
if (bindContext) {
|
||||
return buildJsonDirectResponseAndBindContext(exchange, HttpStatus.OK, null, RespEntity.toJson(code, msg, rid));
|
||||
} else {
|
||||
return buildJsonDirectResponse(exchange, HttpStatus.OK, null, RespEntity.toJson(code, msg, rid));
|
||||
}
|
||||
}
|
||||
|
||||
public static Mono<Void> responseErrorAndBindContext(ServerWebExchange exchange, String filter, int code, String msg) {
|
||||
return responseError(exchange, filter, code, msg, null, true);
|
||||
}
|
||||
|
||||
public static Mono<Void> responseErrorAndBindContext(ServerWebExchange exchange, String filter, int code, String msg, Throwable t) {
|
||||
return responseError(exchange, filter, code, msg, t, true);
|
||||
}
|
||||
|
||||
public static Mono<Void> responseError(ServerWebExchange exchange, int code, String msg) {
|
||||
return responseError(exchange, null, code, msg, null, false);
|
||||
}
|
||||
|
||||
public static Mono<Void> responseError(ServerWebExchange exchange, String reporter, int code, String msg, Throwable t) {
|
||||
return responseError(exchange, reporter, code, msg, t, false);
|
||||
}
|
||||
|
||||
public static String getOriginIp(ServerWebExchange exchange) {
|
||||
String ip = exchange.getAttribute(originIp);
|
||||
if (ip == null) {
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
String v = req.getHeaders().getFirst(xForwardedFor);
|
||||
if (StringUtils.isBlank(v)) {
|
||||
ip = req.getRemoteAddress().getAddress().getHostAddress();
|
||||
} else {
|
||||
ip = StringUtils.split(v, Constants.Symbol.COMMA)[0].trim();
|
||||
if (ip.equalsIgnoreCase(unknown)) {
|
||||
ip = req.getRemoteAddress().getAddress().getHostAddress();
|
||||
} else if (ip.equals(binaryAddress)) {
|
||||
ip = loopBack;
|
||||
}
|
||||
}
|
||||
exchange.getAttributes().put(originIp, ip);
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
70
src/main/java/org/springframework/http/HttpMethod.java
Normal file
70
src/main/java/org/springframework/http/HttpMethod.java
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Java 5 enumeration of HTTP request methods. Intended for use
|
||||
* with {@link org.springframework.http.client.ClientHttpRequest}
|
||||
* and {@link org.springframework.web.client.RestTemplate}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public enum HttpMethod {
|
||||
|
||||
X, GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
|
||||
|
||||
|
||||
private static final Map<String, HttpMethod> mappings = new HashMap<>(16);
|
||||
|
||||
static {
|
||||
for (HttpMethod httpMethod : values()) {
|
||||
mappings.put(httpMethod.name(), httpMethod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve the given method value to an {@code HttpMethod}.
|
||||
* @param method the method value as a String
|
||||
* @return the corresponding {@code HttpMethod}, or {@code null} if not found
|
||||
* @since 4.2.4
|
||||
*/
|
||||
@Nullable
|
||||
public static HttpMethod resolve(@Nullable String method) {
|
||||
return (method != null ? mappings.get(method) : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether this {@code HttpMethod} matches the given
|
||||
* method value.
|
||||
* @param method the method value as a String
|
||||
* @return {@code true} if it matches, {@code false} otherwise
|
||||
* @since 4.2.4
|
||||
*/
|
||||
public boolean matches(String method) {
|
||||
return (this == resolve(method));
|
||||
}
|
||||
|
||||
}
|
||||
1
src/main/resources/META-INF/app.properties
Normal file
1
src/main/resources/META-INF/app.properties
Normal file
@@ -0,0 +1 @@
|
||||
app.id=fizz-gateway
|
||||
5
src/main/resources/apollo-env.properties
Normal file
5
src/main/resources/apollo-env.properties
Normal file
@@ -0,0 +1,5 @@
|
||||
#local.meta=http://localhost:8080
|
||||
#dev.meta=http://localhost:8080
|
||||
#fat.meta=http://localhost:8080
|
||||
#uat.meta=http://localhost:8080
|
||||
#pro.meta=http://localhost:8080
|
||||
29
src/main/resources/application.yml
Normal file
29
src/main/resources/application.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
#apollo:
|
||||
# bootstrap:
|
||||
# enabled: true
|
||||
# namespaces: application
|
||||
# eagerLoad:
|
||||
# enabled: true
|
||||
|
||||
spring.profiles.active: dev
|
||||
|
||||
server.port: 8600
|
||||
|
||||
eureka.instance.prefer-ip-address: true
|
||||
eureka.client.serviceUrl.defaultZone: http://localhost:6600/eureka/
|
||||
spring.application.name: fizz-gateway
|
||||
spring.cloud.loadbalancer.ribbon.enabled: false
|
||||
|
||||
aggregate.redis.host: localhost
|
||||
aggregate.redis.port: 6379
|
||||
aggregate.redis.password: 123456
|
||||
aggregate.redis.database: 10s
|
||||
|
||||
proxy-webclient.name: proxy
|
||||
aggr-webclient.name: aggr
|
||||
|
||||
serviceWhiteList: aservice,bservice,trip-search-common-foundation,trip-mini
|
||||
|
||||
fizz-web-client.timeout: 20000
|
||||
|
||||
log.headers: COOKIE,FIZZ-APPID,FIZZ-SECRETKEY,FIZZ-SIGN,FIZZ-TS,FIZZ-RSV
|
||||
17
src/main/resources/log4j2-spring.xml
Normal file
17
src/main/resources/log4j2-spring.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<Configuration status="info">
|
||||
<properties>
|
||||
<property name="APP_NAME">${sys:APP_NAME}</property>
|
||||
</properties>
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %level %logger{36} - %msg%n" />
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="Console" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.wehotel;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class FizzGatewayApplicationTests {
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user