mirror of
https://github.com/wahyd4/coreutils.git
synced 2026-08-09 04:46:47 +10:00
Initial commit
This commit is contained in:
+74
@@ -0,0 +1,74 @@
|
||||
|
||||
# Created by https://www.gitignore.io/api/eclipse,java,maven
|
||||
|
||||
### Eclipse ###
|
||||
*.pydevproject
|
||||
.metadata
|
||||
.gradle
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
|
||||
### Java ###
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
|
||||
### Maven ###
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Core Utils
|
||||
Utilities to facilitate Java applications development.
|
||||
|
||||
## Master Build Status
|
||||
|
||||
[](https://github.com/grycap/coreutils/blob/master/LICENSE)
|
||||
[](https://travis-ci.org/grycap/coreutils/builds)
|
||||
[](https://coveralls.io/github/grycap/coreutils?branch=master)
|
||||
|
||||
## Environment variables
|
||||
|
||||
``GRYCAP_TESTS_PRINT_OUTPUT`` set value to ``true`` to print tests output.
|
||||
|
||||
## Installation
|
||||
|
||||
### Install from source
|
||||
|
||||
``$ mvn clean install coreutils``
|
||||
|
||||
## Development
|
||||
|
||||
### Run all tests logging to the console
|
||||
|
||||
``$ mvn clean verify -pl coreutils-core -Dgrycap.tests.print.out=true |& tee /tmp/LOGFILE``
|
||||
|
||||
### Run functional and sanity tests logging to the console
|
||||
|
||||
``$ mvn clean test -pl coreutils-core -Dgrycap.tests.print.out=true |& tee /tmp/LOGFILE``
|
||||
|
||||
## Continuous integration
|
||||
|
||||
``$ mvn clean verify coreutils``
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
# Created by https://www.gitignore.io/api/eclipse,java,maven
|
||||
|
||||
### Eclipse ###
|
||||
*.pydevproject
|
||||
.metadata
|
||||
.gradle
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
|
||||
### Java ###
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
|
||||
### Maven ###
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Core Utils - Common Utilities.
|
||||
Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
|
||||
This product combines work with different licenses. See the "NOTICE" text
|
||||
file for details on the various modules and licenses.
|
||||
|
||||
The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.grycap.coreutils</groupId>
|
||||
<artifactId>coreutils</artifactId>
|
||||
<version>0.1.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>coreutils-common</artifactId>
|
||||
<name>Common base utilities</name>
|
||||
<description>Provides utilities to simplify common development tasks</description>
|
||||
<url>https://github.com/grycap/coreutils</url>
|
||||
|
||||
<dependencies>
|
||||
<!-- GRyCAP common utilities -->
|
||||
<dependency>
|
||||
<groupId>org.grycap.coreutils</groupId>
|
||||
<artifactId>coreutils-test</artifactId>
|
||||
<version>${org.grycap.coreutils.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JSR-305: Annotations for Software Defect Detection -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache Commons -->
|
||||
<dependency> <!-- Utility classes for the classes that are considered to be Java standards -->
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency> <!-- I/O utilities -->
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Configuration library -->
|
||||
<dependency>
|
||||
<groupId>com.typesafe</groupId>
|
||||
<artifactId>config</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Core Utils - Common Utilities.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.common;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.trimToNull;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
import com.typesafe.config.ConfigFactory;
|
||||
import com.typesafe.config.ConfigParseOptions;
|
||||
|
||||
/**
|
||||
* Configuration loader.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since @since 0.1.0
|
||||
*/
|
||||
public class Configurer {
|
||||
|
||||
/**
|
||||
* Loads and merges application configuration with default properties.
|
||||
* @param confname - optional configuration filename
|
||||
* @param rootPath - only load configuration properties underneath this path that this code
|
||||
* module owns and understands
|
||||
* @return Configuration loaded from the provided filename or from default properties.
|
||||
*/
|
||||
public Config loadConfig(final @Nullable String confname, final String rootPath) {
|
||||
// load configuration properties
|
||||
Config config;
|
||||
final String confname2 = trimToNull(confname);
|
||||
if (confname2 != null) {
|
||||
final ConfigParseOptions options = ConfigParseOptions.defaults().setAllowMissing(false);
|
||||
final Config customConfig = ConfigFactory.parseFileAnySyntax(new File(confname2), options);
|
||||
final Config regularConfig = ConfigFactory.load();
|
||||
final Config combined = customConfig.withFallback(regularConfig);
|
||||
config = ConfigFactory.load(combined);
|
||||
} else {
|
||||
config = ConfigFactory.load();
|
||||
}
|
||||
// validate
|
||||
config.checkValid(ConfigFactory.defaultReference(), rootPath);
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
# Created by https://www.gitignore.io/api/eclipse,java,maven
|
||||
|
||||
### Eclipse ###
|
||||
*.pydevproject
|
||||
.metadata
|
||||
.gradle
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
|
||||
### Java ###
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
|
||||
### Maven ###
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Core Utils - Fiber-enabled clients.
|
||||
Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
|
||||
This product combines work with different licenses. See the "NOTICE" text
|
||||
file for details on the various modules and licenses.
|
||||
|
||||
The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.grycap.coreutils</groupId>
|
||||
<artifactId>coreutils</artifactId>
|
||||
<version>0.1.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>coreutils-fiber</artifactId>
|
||||
<name>Common clients integrated with green threads (fibers)</name>
|
||||
<description>Provides commonly-used HTTP, REST and other clients, integrated with with green threads (fibers) to minimize the performance impact in virtual environments</description>
|
||||
<url>https://github.com/grycap/coreutils</url>
|
||||
|
||||
<dependencies>
|
||||
<!-- GRyCAP common utilities -->
|
||||
<dependency>
|
||||
<groupId>org.grycap.coreutils</groupId>
|
||||
<artifactId>coreutils-logging</artifactId>
|
||||
<version>${org.grycap.coreutils.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.grycap.coreutils</groupId>
|
||||
<artifactId>coreutils-test</artifactId>
|
||||
<version>${org.grycap.coreutils.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Integrates with Quasar fibers via the Comsat APIs -->
|
||||
<dependency>
|
||||
<groupId>co.paralleluniverse</groupId>
|
||||
<artifactId>quasar-core</artifactId>
|
||||
<classifier>jdk8</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>co.paralleluniverse</groupId>
|
||||
<artifactId>comsat-httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>co.paralleluniverse</groupId>
|
||||
<artifactId>comsat-okhttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- A toolkit for testing multi-threaded code -->
|
||||
<dependency>
|
||||
<groupId>net.jodah</groupId>
|
||||
<artifactId>concurrentunit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- MockServer -->
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-integration-testing</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion> <!-- Prevent SLF4J from complaining about multiple appenders -->
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>silent</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>verbose</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>grycap.tests.print.out</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Maven Surefire Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<logback.configurationFile>${project.build.testOutputDirectory}/logback-test-tmpfile.xml</logback.configurationFile>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Maven Failsafe Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<logback.configurationFile>${project.build.testOutputDirectory}/logback-test-tmpfile.xml</logback.configurationFile>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!-- Prevent Eclipse from executing unnecessary plugins during development -->
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
<artifactId>lifecycle-mapping</artifactId>
|
||||
<version>${m2e.lifecycle-mapping.version}</version>
|
||||
<configuration>
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<versionRange>[${maven-antrun-plugin.version},)</versionRange>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore />
|
||||
</action>
|
||||
</pluginExecution>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<versionRange>[${maven-dependency-plugin.version},)</versionRange>
|
||||
<goals>
|
||||
<goal>properties</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore />
|
||||
</action>
|
||||
</pluginExecution>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-maven-plugin</artifactId>
|
||||
<versionRange>[${mockserver-maven-plugin.version},)</versionRange>
|
||||
<goals>
|
||||
<goal>start</goal>
|
||||
<goal>stop</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore />
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<!-- Compile-time (AoT) instrumentation -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>${maven-antrun-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>instrument-classes</id>
|
||||
<phase>compile</phase>
|
||||
<configuration>
|
||||
<target>
|
||||
<echo message="Using Ahead-of-Time (AoT) to instrument Quasar fibers in compile-time"/>
|
||||
<property name="ant_classpath" refid="maven.dependency.classpath"/>
|
||||
<taskdef name="instrumentationTask" classname="co.paralleluniverse.fibers.instrument.InstrumentationTask" classpath="${ant_classpath}"/>
|
||||
<!-- Use "allowMonitors" to allow instrumenting methods containing synchronization -->
|
||||
<instrumentationTask allowMonitors="true">
|
||||
<fileset dir="${project.build.directory}/classes/" includes="**/*.class"/>
|
||||
</instrumentationTask>
|
||||
</target>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>instrument-test-classes</id>
|
||||
<phase>test-compile</phase>
|
||||
<configuration>
|
||||
<target>
|
||||
<echo message="Using Ahead-of-Time (AoT) to instrument Quasar fibers in compile-time (tests)"/>
|
||||
<property name="ant_classpath" refid="maven.dependency.classpath"/>
|
||||
<taskdef name="instrumentationTask" classname="co.paralleluniverse.fibers.instrument.InstrumentationTask" classpath="${ant_classpath}"/>
|
||||
<!-- Use "allowMonitors" to allow instrumenting methods containing synchronization -->
|
||||
<instrumentationTask allowMonitors="true">
|
||||
<fileset dir="${project.build.directory}/test-classes/" includes="**/*.class"/>
|
||||
</instrumentationTask>
|
||||
</target>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Collect classpath entries as properties in the form groupId:artifactId:type:[classifier] -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>${maven-dependency-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>getClasspathFilenames</id>
|
||||
<goals>
|
||||
<goal>properties</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- MockServer Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<initializationClass>org.grycap.coreutils.fiber.test.mockserver.FiberExpectationInitializer</initializationClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Maven Surefire Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>-Dco.paralleluniverse.fibers.verifyInstrumentation=true</argLine>
|
||||
<argLine>-Dco.paralleluniverse.fibers.disableAgentWarning</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Maven Failsafe Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<configuration>
|
||||
<enableAssertions>false</enableAssertions>
|
||||
<argLine>-Dco.paralleluniverse.fibers.verifyInstrumentation=true</argLine>
|
||||
<argLine>-Dco.paralleluniverse.fibers.disableAgentWarning</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.http;
|
||||
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static java.nio.file.Files.createTempDirectory;
|
||||
import static java.nio.file.attribute.PosixFilePermissions.asFileAttribute;
|
||||
import static java.nio.file.attribute.PosixFilePermissions.fromString;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static org.apache.commons.lang3.StringUtils.trimToNull;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.squareup.okhttp.Cache;
|
||||
import com.squareup.okhttp.CacheControl;
|
||||
import com.squareup.okhttp.Callback;
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.RequestBody;
|
||||
|
||||
import co.paralleluniverse.fibers.okhttp.FiberOkHttpClient;
|
||||
import okio.BufferedSink;
|
||||
|
||||
/**
|
||||
* HTTP+SPDY client.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since @since 0.1.0
|
||||
*/
|
||||
public class Http2Client {
|
||||
|
||||
private static final Logger LOGGER = getLogger(Http2Client.class);
|
||||
|
||||
private static final int CACHE_SIZE_MIB = 32 * 1024 * 1024; // 32 MiB
|
||||
|
||||
private static OkHttpClient __client = null;
|
||||
|
||||
private Lock mutex = new ReentrantLock();
|
||||
|
||||
public static Http2Client getHttp2Client() {
|
||||
return new Http2Client();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link OkHttpClient} instance and configures it with a cache. The same instance is used across the application to
|
||||
* benefit from a common cache storage and to prevent cache corruption. The cache directory is created private to the user who
|
||||
* runs the application and its content is deleted when the JVM starts its shutting down sequence.
|
||||
* @return A {@link OkHttpClient} instance that can be used everywhere in the application.
|
||||
*/
|
||||
private OkHttpClient client() {
|
||||
mutex.lock();
|
||||
try {
|
||||
if (__client == null) {
|
||||
__client = new FiberOkHttpClient();
|
||||
try {
|
||||
final File cacheDir = createTempDirectory("coreutils-okhttp-cache-", asFileAttribute(fromString("rwx------"))).toFile();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
FileUtils.deleteQuietly(cacheDir);
|
||||
}
|
||||
});
|
||||
final Cache cache = new Cache(cacheDir, CACHE_SIZE_MIB);
|
||||
__client.setCache(cache);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Failed to create directory cache", e);
|
||||
}
|
||||
}
|
||||
return __client;
|
||||
} finally {
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve information from a server via a HTTP GET request.
|
||||
* @param url - URL target of this request
|
||||
* @param nocache - don't accept an invalidated cached response, and don't store the server's response in any cache
|
||||
* @param callback - is called back when the response is readable
|
||||
*/
|
||||
public void asyncGet(final String url, final boolean nocache, final Callback callback) {
|
||||
asyncGet(url, null, nocache, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve information from a server via a HTTP GET request.
|
||||
* @param url - URL target of this request
|
||||
* @param nocache - don't accept an invalidated cached response, and don't store the server's response in any cache
|
||||
* @param callback - is called back when the response is readable
|
||||
*/
|
||||
public void asyncGetJson(final String url, final boolean nocache, final Callback callback) {
|
||||
asyncGet(url, newArrayList("application/json"), nocache, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve information from a server via a HTTP GET request.
|
||||
* @param url - URL target of this request
|
||||
* @param acceptableMediaTypes - Content-Types that are acceptable for this request
|
||||
* @param nocache - don't accept an invalidated cached response, and don't store the server's response in any cache
|
||||
* @param callback - is called back when the response is readable
|
||||
*/
|
||||
public void asyncGet(final String url, final @Nullable List<String> acceptableMediaTypes, final boolean nocache, final Callback callback) {
|
||||
final String url2 = requireNonNull(trimToNull(url), "A non-empty URL expected");
|
||||
requireNonNull(callback, "A valid callback expected");
|
||||
// configure cache
|
||||
final CacheControl.Builder cacheControlBuilder = new CacheControl.Builder();
|
||||
if (nocache) cacheControlBuilder.noCache().noStore();
|
||||
else cacheControlBuilder.maxStale(3600, TimeUnit.SECONDS);
|
||||
// prepare request
|
||||
final Request.Builder requestBuilder = new Request.Builder().cacheControl(cacheControlBuilder.build()).url(url2);
|
||||
ofNullable(acceptableMediaTypes).orElse(emptyList()).stream().filter(Objects::nonNull).forEach(type -> requestBuilder.addHeader("Accept", type));
|
||||
// submit request
|
||||
client().newCall(requestBuilder.build()).enqueue(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts data to a server via a HTTP POST request.
|
||||
* @param url - URL target of this request
|
||||
* @param mediaType - Content-Type header for this request
|
||||
* @param supplier - supplies the content of this request
|
||||
* @param callback - is called back when the response is readable
|
||||
*/
|
||||
public void asyncPost(final String url, final String mediaType, final Supplier<String> supplier, final Callback callback) {
|
||||
requireNonNull(supplier, "A valid supplier expected");
|
||||
asyncPostBytes(url, mediaType, () -> ofNullable(supplier.get()).orElse("").getBytes(), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts the content of a buffer of bytes to a server via a HTTP POST request.
|
||||
* @param url - URL target of this request
|
||||
* @param mediaType - Content-Type header for this request
|
||||
* @param supplier - supplies the content of this request
|
||||
* @param callback - is called back when the response is readable
|
||||
*/
|
||||
public void asyncPostBytes(final String url, final String mediaType, final Supplier<byte[]> supplier, final Callback callback) {
|
||||
final String url2 = requireNonNull(trimToNull(url), "A non-empty URL expected");
|
||||
final String mediaType2 = requireNonNull(trimToNull(mediaType), "A non-empty media type expected");
|
||||
requireNonNull(supplier, "A valid supplier expected");
|
||||
requireNonNull(callback, "A valid callback expected");
|
||||
// prepare request
|
||||
final Request request = new Request.Builder().url(url2).post(new RequestBody() {
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return MediaType.parse(mediaType2 + "; charset=utf-8");
|
||||
}
|
||||
@Override
|
||||
public void writeTo(final BufferedSink sink) throws IOException {
|
||||
sink.write(supplier.get());
|
||||
}
|
||||
}).build();
|
||||
// submit request
|
||||
client().newCall(request).enqueue(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts data to a server via a HTTP PUT request.
|
||||
* @param url - URL target of this request
|
||||
* @param mediaType - Content-Type header for this request
|
||||
* @param supplier - supplies the content of this request
|
||||
* @param callback - is called back when the response is readable
|
||||
*/
|
||||
public void asyncPut(final String url, final String mediaType, final Supplier<String> supplier, final Callback callback) {
|
||||
requireNonNull(supplier, "A valid supplier expected");
|
||||
asyncPutBytes(url, mediaType, () -> ofNullable(supplier.get()).orElse("").getBytes(), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the content of a buffer of bytes to a server via a HTTP PUT request.
|
||||
* @param url - URL target of this request
|
||||
* @param mediaType - Content-Type header for this request
|
||||
* @param supplier - supplies the content of this request
|
||||
* @param callback - is called back when the response is readable
|
||||
*/
|
||||
public void asyncPutBytes(final String url, final String mediaType, final Supplier<byte[]> supplier, final Callback callback) {
|
||||
final String url2 = requireNonNull(trimToNull(url), "A non-empty URL expected");
|
||||
final String mediaType2 = requireNonNull(trimToNull(mediaType), "A non-empty media type expected");
|
||||
requireNonNull(supplier, "A valid supplier expected");
|
||||
requireNonNull(callback, "A valid callback expected");
|
||||
// prepare request
|
||||
final Request request = new Request.Builder().url(url2).put(new RequestBody() {
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return MediaType.parse(mediaType2 + "; charset=utf-8");
|
||||
}
|
||||
@Override
|
||||
public void writeTo(final BufferedSink sink) throws IOException {
|
||||
sink.write(supplier.get());
|
||||
}
|
||||
}).build();
|
||||
// submit request
|
||||
client().newCall(request).enqueue(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete HTTP method.
|
||||
* @param supplier - supplies the content of this request
|
||||
* @param callback - is called back when the response is readable
|
||||
*/
|
||||
public void asyncDelete(final String url, final Callback callback) {
|
||||
final String url2 = requireNonNull(trimToNull(url), "A non-empty URL expected");
|
||||
requireNonNull(callback, "A valid callback expected");
|
||||
// prepare request
|
||||
final Request request = new Request.Builder().url(url2).delete().build();
|
||||
// submit request
|
||||
client().newCall(request).enqueue(callback);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.http;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableMap.of;
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
import static java.util.Collections.synchronizedList;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static org.apache.commons.lang3.StringUtils.removeEnd;
|
||||
import static org.apache.commons.lang3.StringUtils.trimToNull;
|
||||
import static org.grycap.coreutils.fiber.net.UrlBuilder.getUrlBuilder;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.utils.URIUtils;
|
||||
import org.apache.http.concurrent.FutureCallback;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
|
||||
import org.apache.http.impl.nio.client.HttpAsyncClients;
|
||||
import org.apache.http.message.BasicHttpRequest;
|
||||
import org.apache.http.nio.client.methods.ZeroCopyConsumer;
|
||||
import org.apache.http.nio.protocol.BasicAsyncRequestProducer;
|
||||
import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
|
||||
import org.grycap.coreutils.fiber.net.UrlBuilder;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import co.paralleluniverse.fibers.httpasyncclient.FiberCloseableHttpAsyncClient;
|
||||
|
||||
/**
|
||||
* High-concurrency data fetcher. Note that this class will name fetched files using a convention-over-configuration pattern
|
||||
* where the suffix <tt>.partial</tt> is appended to the filenames to differentiate between files completely retrieved and
|
||||
* files being fetched. Once the download complete, the file is moved to its final destination, by simply removing the suffix
|
||||
* from the filename and renaming the file.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class HttpDataFetcher {
|
||||
|
||||
private static final Logger LOGGER = getLogger(HttpDataFetcher.class);
|
||||
|
||||
/**
|
||||
* Maximum allowed concurrency. Values above this limit will be trimmed.
|
||||
*/
|
||||
public static final int MAX_CONCURRENCY = 32;
|
||||
|
||||
private final int concurrencyLevel;
|
||||
|
||||
/**
|
||||
* Convenient constructor that allows creating a new instance of this class setting the value of the concurrency level.
|
||||
* Values above this {@link HttpDataFetcher#MAX_CONCURRENCY limit} will be trimmed.
|
||||
* @param concurrencyLevel - desired concurrency level
|
||||
*/
|
||||
public HttpDataFetcher(final int concurrencyLevel) {
|
||||
this.concurrencyLevel = Range.closed(1, MAX_CONCURRENCY).contains(concurrencyLevel) ? concurrencyLevel : MAX_CONCURRENCY;
|
||||
LOGGER.info(new StringBuilder("Concurrency level: ").append(this.concurrencyLevel).toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialization of the method {@link HttpDataFetcher#fetchToDir(URL, String, List, String, String, File)} that allows fetching
|
||||
* and saving a bunch of objects to the specified directory from a server that uses a REST or REST-like API where each object
|
||||
* is retrieved from the URL formed appending the object's identifier to the path of the the base URL.
|
||||
* @param baseUrl - base URL from where the objects will be fetched
|
||||
* @param ids - a list with the identifiers of the all requests that will be attempted
|
||||
* @param outdir - directory where the files will be stored
|
||||
* @return A {@link CompletableFuture} that allows cancellation. Once each fetch operation is completed, its status is updated
|
||||
* in the future with one of the possible values provided by the enumeration {@link FetchStatus}.
|
||||
* @throws IOException If an error occurs during the execution of the method that prevents fetching or saving the files.
|
||||
*/
|
||||
public FecthFuture fetchToDir(final URL baseUrl, final List<String> ids, final File outdir) throws IOException {
|
||||
return fetchToDir(baseUrl, null, ids, null, null, outdir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows fetching and saving a bunch of objects to the specified directory from a server that uses a REST or REST-like API
|
||||
* where each object is retrieved from the URL formed appending the object's identifier to the path of the the base URL, and
|
||||
* optionally from a server that uses a parameter to identify the objects. Supports additional configuration options to name
|
||||
* the fetched objects.
|
||||
* @param baseUrl - base URL from where the objects will be fetched
|
||||
* @param queryParam - if defined, a query parameter will be appended to the base URL with the identifier of the request
|
||||
* @param ids - a list with the identifiers of the all requests that will be attempted
|
||||
* @param prefix - optionally prepend this prefix to the filenames of the saved files
|
||||
* @param suffix - optionally append this suffix to the filenames of the saved files
|
||||
* @param outdir - directory where the files will be stored
|
||||
* @return A {@link CompletableFuture} that allows cancellation. Once each fetch operation is completed, its status is updated
|
||||
* in the future with one of the possible values provided by the enumeration {@link FetchStatus}.
|
||||
* @throws IOException If an error occurs during the execution of the method that prevents fetching or saving the files.
|
||||
*/
|
||||
public FecthFuture fetchToDir(final URL baseUrl, final @Nullable String queryParam, final List<String> ids,
|
||||
final @Nullable String prefix, final @Nullable String suffix, final File outdir) throws IOException {
|
||||
// check mandatory parameters
|
||||
requireNonNull(baseUrl, "A valid URL expected");
|
||||
final FecthFuture toBeCompleted = new FecthFuture(requireNonNull(ids, "A valid list of identifiers expected").stream()
|
||||
.map(StringUtils::trimToNull).filter(Objects::nonNull).distinct().collect(Collectors.toList()));
|
||||
requireNonNull(outdir, "A valid output directory expected");
|
||||
checkArgument((outdir.isDirectory() && outdir.canWrite()) || outdir.mkdirs(), new StringBuilder("Cannot write to the output directory: ")
|
||||
.append(outdir.getAbsolutePath()).toString());
|
||||
// get optional parameters
|
||||
final Optional<String> queryParam2 = ofNullable(trimToNull(queryParam));
|
||||
final String prefix2 = ofNullable(prefix).orElse("");
|
||||
final String suffix2 = ofNullable(suffix).orElse("");
|
||||
try (final CloseableHttpAsyncClient asyncHttpClient = createFiberCloseableHttpAsyncClient()) {
|
||||
asyncHttpClient.start();
|
||||
final UrlBuilder urlBuilder = getUrlBuilder(baseUrl);
|
||||
// an explanation is needed since this code is instrumented by Quasar and Comsat: requests are created during the first part of
|
||||
// this lambda expression (map), but they are not executed until the get() method is called in the second part of the expression
|
||||
// (forEach). Here that parallel stream is used to block and wait for the requests to complete. In case that a single stream is
|
||||
// used, each request will be created and executed sequentially. Therefore, the alternative to parallel stream is to separate
|
||||
// the lambda expression in two loops, creating the requests in the first loop and calling get() in the second one.
|
||||
toBeCompleted.monList.parallelStream().map(m -> {
|
||||
try {
|
||||
// create output file
|
||||
final File outfile = new File(outdir, new StringBuilder(prefix2).append(m.id).append(suffix2).append(".partial").toString());
|
||||
checkState(outfile.createNewFile(), new StringBuilder("Cannot create the output file: ")
|
||||
.append(outfile.getAbsolutePath()).toString());
|
||||
// create the HTTP request
|
||||
final HttpHost target = URIUtils.extractHost(baseUrl.toURI());
|
||||
final HttpRequest request = new BasicHttpRequest("GET", urlBuilder.buildRelativeUrl(queryParam2.isPresent() ? null : m.id,
|
||||
queryParam2.isPresent() ? of(queryParam2.get(), m.id) : null));
|
||||
final HttpAsyncRequestProducer producer = new BasicAsyncRequestProducer(target, request);
|
||||
// create the consumer
|
||||
final ZeroCopyConsumer<File> consumer = new ZeroCopyConsumer<File>(outfile) {
|
||||
@Override
|
||||
protected File process(final HttpResponse response, final File file, final ContentType contentType) throws Exception {
|
||||
final StatusLine status = response.getStatusLine();
|
||||
if (LOGGER.isDebugEnabled())
|
||||
LOGGER.debug(new StringBuilder("Got file: statusCode=").append(status.getStatusCode())
|
||||
.append(", file=").append(file.getAbsolutePath()).toString());
|
||||
if (status.getStatusCode() != HttpStatus.SC_OK)
|
||||
throw new ClientProtocolException(new StringBuilder("Object fetch failed: ").append(status).toString());
|
||||
return file;
|
||||
}
|
||||
};
|
||||
// prepare request
|
||||
m.future = asyncHttpClient.execute(producer, consumer, new FutureCallback<File>() {
|
||||
@Override
|
||||
public void cancelled() {
|
||||
toBeCompleted.update(m.id, FetchStatus.CANCELLED);
|
||||
LOGGER.info("Task cancelled");
|
||||
}
|
||||
@Override
|
||||
public void completed(final File result) {
|
||||
try {
|
||||
final Path path = result.toPath();
|
||||
Files.move(path, path.resolveSibling(removeEnd(result.getName(), ".partial")), REPLACE_EXISTING);
|
||||
toBeCompleted.update(m.id, FetchStatus.COMPLETED);
|
||||
} catch (IOException ex) {
|
||||
toBeCompleted.update(m.id, FetchStatus.FAILED);
|
||||
LOGGER.error("Fecth failed to move file to its final destination with error", ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void failed(final Exception ex) {
|
||||
toBeCompleted.update(m.id, FetchStatus.FAILED);
|
||||
LOGGER.error("Fecth failed with error", ex);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(new StringBuilder("Failed to fetch object with id: ").append(m.id).toString(), e);
|
||||
}
|
||||
return m;
|
||||
}).forEach(m -> {
|
||||
try {
|
||||
// submit requests and wait for completion
|
||||
m.future.get();
|
||||
} catch (Exception ignore) { /* exceptions are handled in the callback functions */ }
|
||||
});
|
||||
}
|
||||
return toBeCompleted;
|
||||
}
|
||||
|
||||
private CloseableHttpAsyncClient createFiberCloseableHttpAsyncClient() {
|
||||
return FiberCloseableHttpAsyncClient.wrap(HttpAsyncClients
|
||||
.custom()
|
||||
.setMaxConnPerRoute(concurrencyLevel)
|
||||
.setMaxConnTotal(concurrencyLevel)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends Java 8 {@link CompletableFuture} with real cancellation that will attempt to cancel the requests managed by this future.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.0.1
|
||||
* @see <a href="https://dzone.com/articles/completablefuture-cant-be">CompletableFuture can't be Interrupted</a>
|
||||
*/
|
||||
public static class FecthFuture extends CompletableFuture<Map<String, FetchStatus>> {
|
||||
|
||||
private final List<FetchMonitor> monList;
|
||||
|
||||
public FecthFuture(final List<String> ids) {
|
||||
this.monList = synchronizedList(requireNonNull(ids, "A non-empty list expected").stream()
|
||||
.map(FetchMonitor::new).collect(Collectors.toList()));
|
||||
checkArgument(!monList.isEmpty(), "A non-empty list expected");
|
||||
}
|
||||
|
||||
public void update(final String id, final FetchStatus newStatus) {
|
||||
monList.stream().filter(m -> m.id.equals(id)).findFirst().ifPresent(m -> {
|
||||
m.status = requireNonNull(newStatus, "A non-empty status expected");
|
||||
});
|
||||
if (!monList.stream().anyMatch(m -> FetchStatus.PENDING.equals(m.status))) {
|
||||
complete(monList.stream().collect(Collectors.toMap(FetchMonitor::getId, FetchMonitor::getStatus)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(final boolean mayInterruptIfRunning) {
|
||||
monList.stream().forEach(m -> { try { m.future.cancel(true); } catch (Exception ignore) { } });
|
||||
return super.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitors fetch operation status.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.0.1
|
||||
*/
|
||||
private static class FetchMonitor {
|
||||
|
||||
private final String id;
|
||||
private Future<File> future;
|
||||
private FetchStatus status = FetchStatus.PENDING;
|
||||
|
||||
public FetchMonitor(final String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public FetchStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible status after object fetching.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public enum FetchStatus {
|
||||
PENDING,
|
||||
COMPLETED,
|
||||
CANCELLED,
|
||||
FAILED
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.net;
|
||||
|
||||
import static java.net.URLDecoder.decode;
|
||||
import static java.nio.charset.Charset.defaultCharset;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.stripEnd;
|
||||
import static org.apache.commons.lang3.StringUtils.trimToNull;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
|
||||
/**
|
||||
* Builder for {@link URL} instances.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class UrlBuilder {
|
||||
|
||||
/**
|
||||
* The base URL that the methods provided by this class will use to create new URLs.
|
||||
*/
|
||||
private final URL baseUrl;
|
||||
|
||||
private UrlBuilder(final URL baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient factory method that creates a new instance of this class.
|
||||
* @param baseUrl - base URL that will be used to create new URLs
|
||||
* @return A new instance of this class, allowing callers to use the methods of this class to create new URLs
|
||||
* from the base URL provided as argument.
|
||||
*/
|
||||
public static UrlBuilder getUrlBuilder(final URL baseUrl) {
|
||||
URL url = null;
|
||||
try {
|
||||
url = new URL(decode(requireNonNull(baseUrl).toString(), defaultCharset().name()));
|
||||
} catch (MalformedURLException | UnsupportedEncodingException e) {
|
||||
throw new IllegalArgumentException(new StringBuilder("Cannot create a URI from the provided URL: ")
|
||||
.append(baseUrl != null ? baseUrl.toString() : "null").toString(), e);
|
||||
}
|
||||
return new UrlBuilder(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new URL relative to the base URL provided in the constructor of this class. The new relative URL
|
||||
* includes the path, query parameters and the internal reference of the {@link UrlBuilder#baseUrl base URL}
|
||||
* provided with this class. An additional fragment, as well as additional query parameters can be optionally
|
||||
* added to the new URL. In addition to the parameters passed to the method as an argument, the supplied
|
||||
* fragment can also include parameters that will be added to the created URL. The created URL is normalized
|
||||
* and unencoded before returning it to the caller. The current implementation has the following limitations:
|
||||
* <ul>
|
||||
* <li>Arrays are not supported: <tt>q=foo&q=bar</tt> will produce an error.</li>
|
||||
* <li>Internal references are only supported in the base URL. Any additional reference provided with the
|
||||
* fragment will be silently ignored: the fragment <tt>/rd#ref</tt> will be appended to the base URL as
|
||||
* <tt>/rd</tt>, ignoring the internal reference.</li>
|
||||
* </ul>
|
||||
* @param fragment - optional URL fragment (may include parameters, but not references) that will be added
|
||||
* to the base URL
|
||||
* @param params - optional query parameters that will be added to the base URL
|
||||
* @return A relative URL created from the base URL provided in the constructor of this class and adding the
|
||||
* fragment and parameters passed as arguments to this method.
|
||||
*/
|
||||
public String buildRelativeUrl(final @Nullable String fragment, final @Nullable Map<String, String> params) {
|
||||
String url = null;
|
||||
final Optional<String> fragment2 = ofNullable(trimToNull(fragment));
|
||||
try {
|
||||
final Optional<URL> fragmentUrl = ofNullable(fragment2.isPresent() ? new URL("http://example.com/" + fragment2.get()) : null);
|
||||
final URIBuilder uriBuilder = new URIBuilder();
|
||||
// add path
|
||||
uriBuilder.setPath(new StringBuilder(ofNullable(trimToNull(baseUrl.getPath())).orElse("/"))
|
||||
.append(fragmentUrl.isPresent() ? "/" + stripEnd(fragmentUrl.get().getPath(), "/") : "")
|
||||
.toString().replaceAll("[/]{2,}", "/"));
|
||||
// add query parameters
|
||||
if (isNotBlank(baseUrl.getQuery())) {
|
||||
uriBuilder.setParameters(URLEncodedUtils.parse(baseUrl.getQuery(), defaultCharset()));
|
||||
}
|
||||
if (fragmentUrl.isPresent() && isNotBlank(fragmentUrl.get().getQuery())) {
|
||||
URLEncodedUtils.parse(fragmentUrl.get().getQuery(), defaultCharset()).stream().forEach(p -> {
|
||||
uriBuilder.addParameter(p.getName(), p.getValue());
|
||||
});
|
||||
}
|
||||
ofNullable(params).orElse(emptyMap()).entrySet().stream().forEach(p -> {
|
||||
uriBuilder.addParameter(p.getKey(), p.getValue());
|
||||
});
|
||||
// add internal reference
|
||||
uriBuilder.setFragment(baseUrl.getRef());
|
||||
// build relative URL
|
||||
url = uriBuilder.build().normalize().toString();
|
||||
} catch (MalformedURLException | URISyntaxException e) {
|
||||
throw new IllegalStateException(new StringBuilder("Failed to create relative URL from provided parameters: fragment=")
|
||||
.append(fragment2.orElse("null")).append(", params=").append(params != null ? params.toString() : "null").toString(), e);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.test;
|
||||
|
||||
import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
|
||||
import static org.grycap.coreutils.fiber.test.mockserver.FiberExpectationInitializer.MOCK_SERVER_BASE_URL;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.grycap.coreutils.fiber.http.HttpDataFetcher;
|
||||
import org.grycap.coreutils.fiber.http.HttpDataFetcher.FetchStatus;
|
||||
import org.grycap.coreutils.test.category.IntegrationTests;
|
||||
import org.grycap.coreutils.test.rules.TestPrinter;
|
||||
import org.grycap.coreutils.test.rules.TestWatcher2;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.rules.TestRule;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import co.paralleluniverse.fibers.futures.AsyncCompletionStage;
|
||||
|
||||
/**
|
||||
* Tests data fetching with cancellation.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
@Category(IntegrationTests.class)
|
||||
public class FetchCancellationTest {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder tmpFolder = new TemporaryFolder();
|
||||
|
||||
@Rule
|
||||
public TestPrinter pw = new TestPrinter();
|
||||
|
||||
@Rule
|
||||
public TestRule watchman = new TestWatcher2(pw);
|
||||
|
||||
@Test
|
||||
public void testFetch() throws Exception {
|
||||
// create fetcher
|
||||
final HttpDataFetcher fetcher = new HttpDataFetcher(2);
|
||||
assertThat("Fetcher was created", fetcher, notNullValue());
|
||||
|
||||
// create output folder
|
||||
final File outDir = tmpFolder.newFolder(randomAlphanumeric(12));
|
||||
assertThat("Output dir was created", outDir, notNullValue());
|
||||
assertThat("Output dir is writable", outDir.canWrite());
|
||||
|
||||
// submit request and cancel
|
||||
final ExecutorService executorService = Executors.newFixedThreadPool(2);
|
||||
final Future<Map<String, FetchStatus>> future = executorService.submit(new Callable<Map<String, FetchStatus>>() {
|
||||
@Override
|
||||
public Map<String, FetchStatus> call() throws Exception {
|
||||
return AsyncCompletionStage.get(fetcher.fetchToDir(new URL(MOCK_SERVER_BASE_URL + "/fetch/long-waiting"),
|
||||
ImmutableList.of("1"), outDir), 120000l, TimeUnit.SECONDS);
|
||||
}
|
||||
});
|
||||
assertThat("Request was cancelled", future.cancel(true));
|
||||
assertThat("File does not exist", not(new File(outDir, "1").exists()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.test;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.apache.commons.collections.ListUtils.union;
|
||||
import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
|
||||
import static org.grycap.coreutils.fiber.test.mockserver.FiberExpectationInitializer.MOCK_SERVER_BASE_URL;
|
||||
import static org.grycap.coreutils.fiber.test.mockserver.ObjectResponseValidator.validateJson;
|
||||
import static org.grycap.coreutils.fiber.test.mockserver.ObjectResponseValidator.validateXml;
|
||||
import static org.hamcrest.CoreMatchers.allOf;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.hasItem;
|
||||
import static org.hamcrest.CoreMatchers.hasItems;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.number.OrderingComparison.greaterThan;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.grycap.coreutils.fiber.http.HttpDataFetcher;
|
||||
import org.grycap.coreutils.fiber.http.HttpDataFetcher.FecthFuture;
|
||||
import org.grycap.coreutils.fiber.http.HttpDataFetcher.FetchStatus;
|
||||
import org.grycap.coreutils.test.category.IntegrationTests;
|
||||
import org.grycap.coreutils.test.rules.TestPrinter;
|
||||
import org.grycap.coreutils.test.rules.TestWatcher2;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameter;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import co.paralleluniverse.fibers.futures.AsyncCompletionStage;
|
||||
|
||||
/**
|
||||
* Tests data fetching.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
@Category(IntegrationTests.class)
|
||||
public class FetchTest {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder tmpFolder = new TemporaryFolder();
|
||||
|
||||
@Rule
|
||||
public TestPrinter pw = new TestPrinter();
|
||||
|
||||
@Rule
|
||||
public TestRule watchman = new TestWatcher2(pw);
|
||||
|
||||
/**
|
||||
* Provides an input dataset with different data formats (JSON, XML) and different access methods (URL fragment,
|
||||
* query parameter). Some tests will surpass the concurrency level. Invalid identifiers are included to test that
|
||||
* the fetcher handles 404 status responses.
|
||||
* @return Parameters for the different test scenarios.
|
||||
*/
|
||||
@Parameters(name = "{index}: path={0}, queryParam={1}, validIds={2}, invalidIds={3}, concurrencyLevel={4}, useFiber={5}")
|
||||
public static Collection<Object[]> data() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
/*0*/ { "/test/json", null, ImmutableList.of("1", "2"), emptyList(), 4, true },
|
||||
/*1*/ { "/test/xml", "q", ImmutableList.of("1", "2"), emptyList(), 8, true },
|
||||
/*2*/ { "/test/json", null, emptyList(), ImmutableList.of("3", "4"), 4, true },
|
||||
/*3*/ { "/test/json", null, ImmutableList.of("1", "2"), ImmutableList.of("3", "4"), 4, true },
|
||||
/*4*/ { "/test/xml", "q", ImmutableList.of("1", "2"), ImmutableList.of("5", "6", "7"), 2, true },
|
||||
/*5*/ { "/test/json", null, ImmutableList.of("1", "2"), emptyList(), 8, false }
|
||||
});
|
||||
}
|
||||
|
||||
@Parameter(value = 0) public String path;
|
||||
@Parameter(value = 1) public String queryParam;
|
||||
@Parameter(value = 2) public List<String> validIds;
|
||||
@Parameter(value = 3) public List<String> invalidIds;
|
||||
@Parameter(value = 4) public int concurrencyLevel;
|
||||
@Parameter(value = 5) public boolean useFiber;
|
||||
|
||||
@Test
|
||||
public void testFetch() throws Exception {
|
||||
// create fetcher
|
||||
final HttpDataFetcher fetcher = new HttpDataFetcher(concurrencyLevel);
|
||||
assertThat("Fetcher was created", fetcher, notNullValue());
|
||||
|
||||
// create output folder
|
||||
final File outDir = tmpFolder.newFolder(randomAlphanumeric(12));
|
||||
assertThat("Output dir was created", outDir, notNullValue());
|
||||
assertThat("Output dir is writable", outDir.canWrite());
|
||||
|
||||
// create test dataset
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<String> ids = union(validIds, invalidIds);
|
||||
final URL url = new URL(MOCK_SERVER_BASE_URL + path);
|
||||
|
||||
// fetch from server
|
||||
Map<String, FetchStatus> results = null;
|
||||
if (useFiber) {
|
||||
results = AsyncCompletionStage.get(fetcher.fetchToDir(url, queryParam, ids, "file-", ".tmp", outDir), 30l, TimeUnit.SECONDS);
|
||||
} else {
|
||||
final FecthFuture toBeCompleted = fetcher.fetchToDir(url, queryParam, ids, null, null, outDir);
|
||||
assertThat("Fetch task was created", toBeCompleted, notNullValue());
|
||||
results = toBeCompleted.get(30l, TimeUnit.SECONDS);
|
||||
}
|
||||
assertThat("Results are available", results, notNullValue());
|
||||
assertThat("Result count coincides with expected", results.size(), equalTo(ids.size()));
|
||||
assertThat("Ids coincides with expected", results.keySet(), hasItems(ids.toArray(new String[ids.size()])));
|
||||
pw.println(" >> Results: " + results);
|
||||
if (!validIds.isEmpty()) {
|
||||
assertThat("Status coincides with expected", results.values(), allOf(hasItem(FetchStatus.COMPLETED),
|
||||
not(hasItems(FetchStatus.PENDING, FetchStatus.CANCELLED))));
|
||||
}
|
||||
if (!invalidIds.isEmpty()) {
|
||||
assertThat("Status coincides with expected", results.values(), allOf(hasItem(FetchStatus.FAILED),
|
||||
not(hasItems(FetchStatus.PENDING, FetchStatus.CANCELLED))));
|
||||
}
|
||||
|
||||
// check files in the output directory
|
||||
for (final String id : validIds) {
|
||||
final File outFile = new File(outDir, useFiber ? new StringBuilder("file-").append(id).append(".tmp").toString() : id);
|
||||
assertThat("Output file was created", outDir, notNullValue());
|
||||
assertThat("Output file file exists and is redable", outFile.canRead(), equalTo(true));
|
||||
assertThat("Output file file is not empty", outFile.length(), greaterThan(0l));
|
||||
if (path.contains("xml")) {
|
||||
validateXml(outFile, id);
|
||||
} else if (path.contains("json")) {
|
||||
validateJson(outFile, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.test;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
|
||||
import static org.grycap.coreutils.fiber.test.mockserver.FiberExpectationInitializer.MOCK_SERVER_BASE_URL;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import org.grycap.coreutils.fiber.http.HttpDataFetcher;
|
||||
import org.grycap.coreutils.test.category.SanityTests;
|
||||
import org.grycap.coreutils.test.rules.TestPrinter;
|
||||
import org.grycap.coreutils.test.rules.TestWatcher2;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.rules.TestRule;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* Tests fetcher with invalid inputs.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
@Category(SanityTests.class)
|
||||
public class FetchValidationTest {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder tmpFolder = new TemporaryFolder();
|
||||
|
||||
@Rule
|
||||
public TestPrinter pw = new TestPrinter();
|
||||
|
||||
@Rule
|
||||
public TestRule watchman = new TestWatcher2(pw);
|
||||
|
||||
@Test(expected=NullPointerException.class)
|
||||
public void testFetchWithoutUrl() throws Exception {
|
||||
final HttpDataFetcher fetcher = new HttpDataFetcher(2);
|
||||
assertThat("Fetcher was created", fetcher, notNullValue());
|
||||
fetcher.fetchToDir(null, ImmutableList.of("1"), tmpFolder.newFolder(randomAlphanumeric(12)));
|
||||
}
|
||||
|
||||
@Test(expected=NullPointerException.class)
|
||||
public void testFetchWithoutList() throws Exception {
|
||||
final HttpDataFetcher fetcher = new HttpDataFetcher(2);
|
||||
assertThat("Fetcher was created", fetcher, notNullValue());
|
||||
fetcher.fetchToDir(new URL(MOCK_SERVER_BASE_URL + "/test/json"), null, tmpFolder.newFolder(randomAlphanumeric(12)));
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void testFetchWithEmptyList() throws Exception {
|
||||
final HttpDataFetcher fetcher = new HttpDataFetcher(2);
|
||||
assertThat("Fetcher was created", fetcher, notNullValue());
|
||||
fetcher.fetchToDir(new URL(MOCK_SERVER_BASE_URL + "/test/json"), emptyList(), tmpFolder.newFolder(randomAlphanumeric(12)));
|
||||
}
|
||||
|
||||
@Test(expected=NullPointerException.class)
|
||||
public void testFetchWithoutOutputDir() throws Exception {
|
||||
final HttpDataFetcher fetcher = new HttpDataFetcher(2);
|
||||
assertThat("Fetcher was created", fetcher, notNullValue());
|
||||
fetcher.fetchToDir(new URL(MOCK_SERVER_BASE_URL + "/test/json"), ImmutableList.of("1"), null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.test;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.of;
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static org.grycap.coreutils.fiber.http.Http2Client.getHttp2Client;
|
||||
import static org.grycap.coreutils.fiber.test.mockserver.FiberExpectationInitializer.MOCK_SERVER_BASE_URL;
|
||||
import static org.grycap.coreutils.fiber.test.mockserver.ObjectResponseValidator.isValidJson;
|
||||
import static org.grycap.coreutils.fiber.test.mockserver.ObjectResponseValidator.isValidXml;
|
||||
import static org.hamcrest.CoreMatchers.allOf;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.grycap.coreutils.fiber.http.Http2Client;
|
||||
import org.grycap.coreutils.test.category.IntegrationTests;
|
||||
import org.grycap.coreutils.test.rules.TestPrinter;
|
||||
import org.grycap.coreutils.test.rules.TestWatcher2;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameter;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import com.squareup.okhttp.Callback;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
/**
|
||||
* Tests the {@link Http2Client}.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since @since 0.1.0
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
@Category(IntegrationTests.class)
|
||||
public class Http2ClientTest {
|
||||
|
||||
@Rule
|
||||
public TestPrinter pw = new TestPrinter();
|
||||
|
||||
@Rule
|
||||
public TestRule watchman = new TestWatcher2(pw);
|
||||
|
||||
/**
|
||||
* Provides an input dataset with different data formats (JSON, XML) and different access methods (URL fragment,
|
||||
* query parameter). Some tests will surpass the concurrency level.
|
||||
* @return Parameters for the different test scenarios.
|
||||
*/
|
||||
@Parameters(name = "{index}: method={0}, path={1}, objectId={2}, contentType={3}, nocache={4}")
|
||||
public static Collection<Object[]> data() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
/* 0*/ { "GET", "/test/json/1", "1", of("application/json"), true },
|
||||
/* 1*/ { "GET", "/test/json/2", "2", of("application/json"), true },
|
||||
/* 2*/ { "GET", "/test/xml/1", "1", of("application/xml"), true },
|
||||
/* 3*/ { "GET", "/test/xml/2", "2", of("application/xml"), true },
|
||||
/* 4*/ { "GET", "/test/json?q=1", "1", of("application/json"), true },
|
||||
/* 5*/ { "GET", "/test/json?q=2", "2", of("application/json"), true },
|
||||
/* 6*/ { "GET", "/test/xml?q=1", "1", of("application/xml"), true },
|
||||
/* 7*/ { "GET", "/test/xml?q=2", "2", of("application/xml"), true },
|
||||
/* 8*/ { "GET", "/test/json/1", "1", null, true },
|
||||
/* 9*/ { "GET", "/test/xml/1", "1", null, true },
|
||||
/*10*/ { "GET", "/test/json/1", "1", null, false },
|
||||
/*11*/ { "GET", "/test/xml/1", "1", null, false },
|
||||
/*12*/ { "POST", "/test/json", "X", of("application/json"), true },
|
||||
/*13*/ { "POST", "/test/xml", "Y", of("application/xml"), true },
|
||||
/*14*/ { "PUT", "/test/json/1", "1", of("application/json"), true },
|
||||
/*15*/ { "PUT", "/test/xml/2", "2", of("application/xml"), true },
|
||||
/*16*/ { "DELETE", "/test/json/2", "2", of("application/json"), true },
|
||||
/*17*/ { "DELETE", "/test/xml/1", "1", of("application/xml"), true }
|
||||
});
|
||||
}
|
||||
|
||||
@Parameter(value = 0) public String method;
|
||||
@Parameter(value = 1) public String path;
|
||||
@Parameter(value = 2) public String objectId;
|
||||
@Parameter(value = 3) public List<String> contentType;
|
||||
@Parameter(value = 4) public boolean nocache;
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
// create the client
|
||||
final Http2Client client = getHttp2Client();
|
||||
assertThat("HTTP+SPDY client was created", client, notNullValue());
|
||||
|
||||
// prepare the test
|
||||
final String url = new StringBuilder(MOCK_SERVER_BASE_URL).append(path).toString();
|
||||
final Waiter waiter = new Waiter();
|
||||
|
||||
// submit request
|
||||
switch (method) {
|
||||
case "POST":
|
||||
submitPost(client, url, waiter);
|
||||
break;
|
||||
case "PUT":
|
||||
submitPut(client, url, waiter);
|
||||
break;
|
||||
case "DELETE":
|
||||
submitDelete(client, url, waiter);
|
||||
break;
|
||||
case "GET":
|
||||
default:
|
||||
submitGet(client, url, waiter);
|
||||
break;
|
||||
}
|
||||
waiter.await(30l, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void submitGet(final Http2Client client, final String url, final Waiter waiter) {
|
||||
client.asyncGet(url, contentType, nocache, new Callback() {
|
||||
@Override
|
||||
public void onResponse(final Response response) throws IOException {
|
||||
waiter.assertTrue(response.isSuccessful());
|
||||
final String payload = response.body().source().readUtf8();
|
||||
waiter.assertThat(payload, allOf(notNullValue(), not(equalTo(""))));
|
||||
if (path.contains("xml")) {
|
||||
isValidXml(payload, objectId);
|
||||
} else if (path.contains("json")) {
|
||||
isValidJson(payload, objectId);
|
||||
}
|
||||
waiter.resume();
|
||||
}
|
||||
@Override
|
||||
public void onFailure(final Request request, final IOException throwable) {
|
||||
waiter.fail(throwable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void submitPost(final Http2Client client, final String url, final Waiter waiter) {
|
||||
Supplier<String> supplier = null;
|
||||
if (path.contains("xml")) {
|
||||
supplier = () -> "<object>Y</object>";
|
||||
} else if (path.contains("json")) {
|
||||
supplier = () -> "{ \"object\" : \"X\" }";
|
||||
}
|
||||
client.asyncPost(url, ofNullable(contentType).orElse(newArrayList("application/json")).get(0), supplier, new Callback() {
|
||||
@Override
|
||||
public void onResponse(final Response response) throws IOException {
|
||||
waiter.assertTrue(response.isSuccessful());
|
||||
waiter.resume();
|
||||
}
|
||||
@Override
|
||||
public void onFailure(final Request request, final IOException throwable) {
|
||||
waiter.fail(throwable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void submitPut(final Http2Client client, final String url, final Waiter waiter) {
|
||||
Supplier<String> supplier = null;
|
||||
if (path.contains("xml")) {
|
||||
supplier = () -> "<object>Y</object>";
|
||||
} else if (path.contains("json")) {
|
||||
supplier = () -> "{ \"object\" : \"X\" }";
|
||||
}
|
||||
client.asyncPut(url, ofNullable(contentType).orElse(newArrayList("application/json")).get(0), supplier, new Callback() {
|
||||
@Override
|
||||
public void onResponse(final Response response) throws IOException {
|
||||
waiter.assertTrue(response.isSuccessful());
|
||||
waiter.resume();
|
||||
}
|
||||
@Override
|
||||
public void onFailure(final Request request, final IOException throwable) {
|
||||
waiter.fail(throwable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void submitDelete(final Http2Client client, final String url, final Waiter waiter) {
|
||||
client.asyncDelete(url, new Callback() {
|
||||
@Override
|
||||
public void onResponse(final Response response) throws IOException {
|
||||
waiter.assertTrue(response.isSuccessful());
|
||||
waiter.resume();
|
||||
}
|
||||
@Override
|
||||
public void onFailure(final Request request, final IOException throwable) {
|
||||
waiter.fail(throwable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.test;
|
||||
|
||||
import static com.google.common.collect.ImmutableMap.of;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static org.apache.commons.lang3.StringUtils.trim;
|
||||
import static org.grycap.coreutils.fiber.net.UrlBuilder.getUrlBuilder;
|
||||
import static org.hamcrest.CoreMatchers.allOf;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.grycap.coreutils.fiber.net.UrlBuilder;
|
||||
import org.grycap.coreutils.test.category.SanityTests;
|
||||
import org.grycap.coreutils.test.rules.TestPrinter;
|
||||
import org.grycap.coreutils.test.rules.TestWatcher2;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameter;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
/**
|
||||
* Tests {@link UrlBuilder}.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
@Category(SanityTests.class)
|
||||
public class UrlBuilderTest {
|
||||
|
||||
@Rule
|
||||
public TestPrinter pw = new TestPrinter();
|
||||
|
||||
@Rule
|
||||
public TestRule watchman = new TestWatcher2(pw);
|
||||
|
||||
/**
|
||||
* Provides different input datasets.
|
||||
* @return Parameters for the different test scenarios.
|
||||
*/
|
||||
@Parameters(name = "{index}: baseUrl={0}, fragment={1}, query={2}, expected={3}")
|
||||
public static Collection<Object[]> data() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
/* 0*/ { "http://example.com", "", emptyMap(), "/" },
|
||||
/* 1*/ { "http://example.com", "rd", emptyMap(), "/rd" },
|
||||
/* 2*/ { "http://example.com", "", of("q", "foo"), "/?q=foo" },
|
||||
/* 3*/ { "http://example.com", "rd", of("q", "foo"), "/rd?q=foo" },
|
||||
/* 4*/ { "http://example.com", "/rd", of("q", "foo"), "/rd?q=foo" },
|
||||
/* 5*/ { "http://example.com", "/rd1/rd2", of("q", "foo"), "/rd1/rd2?q=foo" },
|
||||
/* 6*/ { "http://example.com", "/rd1/rd2#ref", of("q1", "foo", "q2", "bar"), "/rd1/rd2?q1=foo&q2=bar" },
|
||||
/* 7*/ { "http://example.com", "/rd?q1=foo#ref", of("q2", "bar"), "/rd?q1=foo&q2=bar" },
|
||||
/* 8*/ { "http://example.com", "/rd?q1=foo&q2=bar#ref", of("q3", "baz"), "/rd?q1=foo&q2=bar&q3=baz" },
|
||||
/* 9*/ { "http://example.com/rd1", "rd2#ref", of("q1", "foo"), "/rd1/rd2?q1=foo" },
|
||||
/*10*/ { "http://example.com/rd1", "/rd2#ref", of("q1", "foo"), "/rd1/rd2?q1=foo" },
|
||||
/*11*/ { "http://example.com/rd1", "/rd2", of("q1", "foo"), "/rd1/rd2?q1=foo" },
|
||||
/*12*/ { "http://example.com/rd1", "/rd2", emptyMap(), "/rd1/rd2" },
|
||||
/*13*/ { "http://example.com?q1=foo", "rd", of("q2", "bar"), "/rd?q1=foo&q2=bar" },
|
||||
/*14*/ { "http://example.com?q1=foo#ref", "rd", of("q2", "bar"), "/rd?q1=foo&q2=bar#ref" },
|
||||
/*15*/ { "http://example.com#ref", "rd", of("q", "foo"), "/rd?q=foo#ref" },
|
||||
/*16*/ { "http://example.com/rd?q=foo#ref", "", of("q", "bar"), "/rd?q=foo&q=bar#ref" },
|
||||
/*17*/ { "http://example.com#foo=bar", "rd", emptyMap(), "/rd#foo=bar" },
|
||||
/*18*/ { "http://example.com/rd1%3Fq1%3Dfoo%26q2%3Dbar%23ref", "/rd2", of("q3", "baz"), "/rd1/rd2?q1=foo&q2=bar&q3=baz#ref" },
|
||||
/*19*/ { "http://example.com/hello+world=foo%26bar", "rd", emptyMap(), "/hello%20world=foo&bar/rd"}
|
||||
// unsupported: { "http://example.com/rd", "", of("q", "foo", "q", "bar"), "/rd?q=foo&q=bar" },
|
||||
});
|
||||
}
|
||||
|
||||
@Parameter(value = 0) public String baseUrl;
|
||||
@Parameter(value = 1) public String fragment;
|
||||
@Parameter(value = 2) public Map<String, String> query;
|
||||
@Parameter(value = 3) public String expected;
|
||||
|
||||
@Test
|
||||
public void testUrlBuilder() throws Exception {
|
||||
// create URL builder
|
||||
final UrlBuilder urlBuilder = getUrlBuilder(new URL(baseUrl));
|
||||
assertThat("URL builder was created", urlBuilder, notNullValue());
|
||||
|
||||
// test URL
|
||||
final String url = urlBuilder.buildRelativeUrl(fragment, query);
|
||||
assertThat("URL coincides with expected", trim(url), allOf(notNullValue(), equalTo(expected)));
|
||||
pw.println("URL: " + url);
|
||||
}
|
||||
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.test.mockserver;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.mockserver.model.Parameter;
|
||||
|
||||
/**
|
||||
* Base definition of response expectations.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class AbstractExpectationResponse {
|
||||
|
||||
private final String path;
|
||||
private final List<Parameter> parameters;
|
||||
private final int statusCode;
|
||||
private final String contentType;
|
||||
private final String body;
|
||||
private final long delay;
|
||||
|
||||
/**
|
||||
* Convenient constructor that receives initial values for all the parameters of this class.
|
||||
* @param path - the path where clients expect the server is listening for incoming requests
|
||||
* @param parameters - query parameters accompanying the request
|
||||
* @param statusCode - the response status code after the request is processed in the server
|
||||
* @param contentType - the content type of the server response
|
||||
* @param body - the content of the body sent with the server response
|
||||
* @param delay - the server will wait this time before sending a response to the client
|
||||
*/
|
||||
public AbstractExpectationResponse(final String path, final Map<String, String> parameters, final int statusCode, final String contentType, final String body, final long delay) {
|
||||
this.path = path;
|
||||
this.parameters = parameters.entrySet().stream().map(p -> {
|
||||
return new Parameter(p.getKey(), p.getValue());
|
||||
}).collect(Collectors.toList());
|
||||
this.statusCode = statusCode;
|
||||
this.contentType = contentType;
|
||||
this.body = body;
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public List<Parameter> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public long getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.test.mockserver;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Defines expectation responses for requests made with the HTTP GET method.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class ExpectationHttpGetResponse extends AbstractExpectationResponse {
|
||||
|
||||
/**
|
||||
* Convenient constructor that can be used to create expectations where the server where the server responds
|
||||
* to all correct requests with the HTTP status code OK (200) to the proper requests and no query parameters
|
||||
* are submitted. This pattern is commonly used in REST and REST-like APIs.
|
||||
* @param path - the path where clients expect the server is listening for incoming requests
|
||||
* @param contentType - the content type of the server response
|
||||
* @param body - the content of the body sent with the server response
|
||||
* @param delay - the server will wait this time before sending a response to the client
|
||||
*/
|
||||
public ExpectationHttpGetResponse(final String path, final String contentType, final String body, final long delay) {
|
||||
super(path, emptyMap(), 200, contentType, body, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor that can be used to create expectations where the server responds
|
||||
* to all correct requests with the HTTP status code OK (200).
|
||||
* @param path - the path where clients expect the server is listening for incoming requests
|
||||
* @param parameters - query parameters accompanying the request
|
||||
* @param contentType - the content type of the server response
|
||||
* @param body - the content of the body sent with the server response
|
||||
* @param delay - the server will wait this time before sending a response to the client
|
||||
*/
|
||||
public ExpectationHttpGetResponse(final String path, final Map<String, String> parameters, final String contentType, final String body, final long delay) {
|
||||
super(path, parameters, 200, contentType, body, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor that receives initial values for all the parameters of this class.
|
||||
* @param path - the path where clients expect the server is listening for incoming requests
|
||||
* @param parameters - query parameters accompanying the request
|
||||
* @param statusCode - the response status code after the request is processed in the server
|
||||
* @param contentType - the content type of the server response
|
||||
* @param body - the content of the body sent with the server response
|
||||
* @param delay - the server will wait this time before sending a response to the client
|
||||
*/
|
||||
public ExpectationHttpGetResponse(final String path, final Map<String, String> parameters, final int statusCode, final String contentType, final String body, final long delay) {
|
||||
super(path, parameters, statusCode, contentType, body, delay);
|
||||
}
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.test.mockserver;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Defines expectation responses for requests made with the HTTP POST method.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class ExpectationHttpPostResponse extends AbstractExpectationResponse {
|
||||
|
||||
/**
|
||||
* Convenient constructor that can be used to create expectations where the server where the server responds
|
||||
* to all correct requests with the HTTP status code CREATED (201) to the proper requests and no query parameters
|
||||
* are submitted. This pattern is commonly used in REST and REST-like APIs.
|
||||
* @param path - the path where clients expect the server is listening for incoming requests
|
||||
* @param contentType - the content type of the server response
|
||||
* @param body - the content of the body sent with the server response
|
||||
* @param delay - the server will wait this time before sending a response to the client
|
||||
*/
|
||||
public ExpectationHttpPostResponse(final String path, final String contentType, final String body, final long delay) {
|
||||
super(path, emptyMap(), 201, contentType, body, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor that can be used to create expectations where the server responds
|
||||
* to all correct requests with the HTTP status code CREATED (201).
|
||||
* @param path - the path where clients expect the server is listening for incoming requests
|
||||
* @param parameters - query parameters accompanying the request
|
||||
* @param contentType - the content type of the server response
|
||||
* @param body - the content of the body sent with the server response
|
||||
* @param delay - the server will wait this time before sending a response to the client
|
||||
*/
|
||||
public ExpectationHttpPostResponse(final String path, final Map<String, String> parameters, final String contentType, final String body, final long delay) {
|
||||
super(path, parameters, 201, contentType, body, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor that receives initial values for all the parameters of this class.
|
||||
* @param path - the path where clients expect the server is listening for incoming requests
|
||||
* @param parameters - query parameters accompanying the request
|
||||
* @param statusCode - the response status code after the request is processed in the server
|
||||
* @param contentType - the content type of the server response
|
||||
* @param body - the content of the body sent with the server response
|
||||
* @param delay - the server will wait this time before sending a response to the client
|
||||
*/
|
||||
public ExpectationHttpPostResponse(final String path, final Map<String, String> parameters, final int statusCode, final String contentType, final String body, final long delay) {
|
||||
super(path, parameters, statusCode, contentType, body, delay);
|
||||
}
|
||||
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.test.mockserver;
|
||||
|
||||
import static com.google.common.collect.ImmutableMap.of;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static org.grycap.coreutils.fiber.test.mockserver.ObjectResponseFactory.jsonObject1;
|
||||
import static org.grycap.coreutils.fiber.test.mockserver.ObjectResponseFactory.jsonObject2;
|
||||
import static org.grycap.coreutils.fiber.test.mockserver.ObjectResponseFactory.xmlObject1;
|
||||
import static org.grycap.coreutils.fiber.test.mockserver.ObjectResponseFactory.xmlObject2;
|
||||
import static org.mockserver.matchers.Times.exactly;
|
||||
import static org.mockserver.model.HttpRequest.request;
|
||||
import static org.mockserver.model.HttpResponse.response;
|
||||
import static org.mockserver.model.StringBody.exact;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.mockserver.client.server.MockServerClient;
|
||||
import org.mockserver.initialize.ExpectationInitializer;
|
||||
import org.mockserver.model.Delay;
|
||||
import org.mockserver.model.Header;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* Sets Mock Server expectations for fetching tests.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class FiberExpectationInitializer implements ExpectationInitializer {
|
||||
|
||||
public static final String MOCK_SERVER_BASE_URL = "http://localhost:9080";
|
||||
|
||||
@Override
|
||||
public void initializeExpectations(final MockServerClient client) {
|
||||
// add GET operations
|
||||
new ImmutableList.Builder<ExpectationHttpGetResponse>()
|
||||
// REST + JSON
|
||||
.add(new ExpectationHttpGetResponse("/test/json/ids", "text/plain", "1,2", 0l))
|
||||
.add(new ExpectationHttpGetResponse("/test/json/1", "application/json", jsonObject1(), 300l))
|
||||
.add(new ExpectationHttpGetResponse("/test/json/2", "application/json", jsonObject2(), 500l))
|
||||
// REST + XML
|
||||
.add(new ExpectationHttpGetResponse("/test/xml/ids", "text/plain", "1,2", 0l))
|
||||
.add(new ExpectationHttpGetResponse("/test/xml/1", "application/xml", xmlObject1(), 0l))
|
||||
.add(new ExpectationHttpGetResponse("/test/xml/2", "application/xml", xmlObject2(), 0l))
|
||||
// Query parameter + JSON
|
||||
.add(new ExpectationHttpGetResponse("/test/json", of("q", "1"), "application/json", jsonObject1(), 100l))
|
||||
.add(new ExpectationHttpGetResponse("/test/json", of("q", "2"), "application/json", jsonObject2(), 0l))
|
||||
// Query parameter + XML
|
||||
.add(new ExpectationHttpGetResponse("/test/xml", of("q", "1"), "application/xml", xmlObject1(), 300l))
|
||||
.add(new ExpectationHttpGetResponse("/test/xml", of("q", "2"), "application/xml", xmlObject2(), 120l))
|
||||
// Very long awaiting time
|
||||
.add(new ExpectationHttpGetResponse("/test/long-waiting/1", "text/plain", "Hello World!", 20000l))
|
||||
.build().stream().forEach(r -> {
|
||||
client.when(request().withMethod("GET").withPath(r.getPath()).withQueryStringParameters(r.getParameters()))
|
||||
.respond(response().withStatusCode(r.getStatusCode())
|
||||
.withHeaders(new Header("Content-Type", r.getContentType() + "; charset=utf-8"),
|
||||
new Header("Cache-Control", "public, max-age=86400"))
|
||||
.withBody(r.getBody())
|
||||
.withDelay(new Delay(TimeUnit.MILLISECONDS, r.getDelay())));
|
||||
});
|
||||
// add POST operations
|
||||
new ImmutableList.Builder<ExpectationHttpPostResponse>()
|
||||
.add(new ExpectationHttpPostResponse("/test/json", "application/json", "{ \"object\" : \"X\" }", 0l))
|
||||
.add(new ExpectationHttpPostResponse("/test/xml", "application/xml", "<object>Y</object>", 0l))
|
||||
.build().stream().forEach(r -> {
|
||||
client.when(request().withMethod("POST").withPath(r.getPath()).withQueryStringParameters(r.getParameters())
|
||||
.withBody(exact(r.getBody())), exactly(1))
|
||||
.respond(response().withStatusCode(r.getStatusCode())
|
||||
.withHeaders(new Header("Cache-Control", "public, max-age=86400"),
|
||||
new Header("Location", r.getPath() + "/createdId"))
|
||||
.withDelay(new Delay(TimeUnit.MILLISECONDS, r.getDelay())));
|
||||
});
|
||||
// add PUT operations
|
||||
new ImmutableList.Builder<AbstractExpectationResponse>()
|
||||
.add(new AbstractExpectationResponse("/test/json/1", emptyMap(), 204, "application/json", "{ \"object\" : \"X\" }", 0l){ })
|
||||
.add(new AbstractExpectationResponse("/test/json/2", emptyMap(), 204, "application/json", "{ \"object\" : \"X\" }", 0l){ })
|
||||
.add(new AbstractExpectationResponse("/test/xml/1", emptyMap(), 204, "application/xml", "<object>Y</object>", 0l){ })
|
||||
.add(new AbstractExpectationResponse("/test/xml/2", emptyMap(), 204, "application/xml", "<object>Y</object>", 0l){ })
|
||||
.build().stream().forEach(r -> {
|
||||
client.when(request().withMethod("PUT").withPath(r.getPath()).withQueryStringParameters(r.getParameters())
|
||||
.withBody(exact(r.getBody())), exactly(1))
|
||||
.respond(response().withStatusCode(r.getStatusCode())
|
||||
.withHeaders(new Header("Cache-Control", "public, max-age=86400"))
|
||||
.withDelay(new Delay(TimeUnit.MILLISECONDS, r.getDelay())));
|
||||
});
|
||||
// add DELETE operations
|
||||
new ImmutableList.Builder<AbstractExpectationResponse>()
|
||||
.add(new AbstractExpectationResponse("/test/json/1", emptyMap(), 204, "application/json", jsonObject1(), 0l){ })
|
||||
.add(new AbstractExpectationResponse("/test/json/2", emptyMap(), 204, "application/json", jsonObject2(), 0l){ })
|
||||
.add(new AbstractExpectationResponse("/test/xml/1", emptyMap(), 204, "application/xml", xmlObject1(), 0l){ })
|
||||
.add(new AbstractExpectationResponse("/test/xml/2", emptyMap(), 204, "application/xml", xmlObject2(), 0l){ })
|
||||
.build().stream().forEach(r -> {
|
||||
client.when(request().withMethod("DELETE").withPath(r.getPath()).withQueryStringParameters(r.getParameters()),
|
||||
exactly(1))
|
||||
.respond(response().withStatusCode(r.getStatusCode())
|
||||
.withHeaders(new Header("Cache-Control", "public, max-age=86400"))
|
||||
.withDelay(new Delay(TimeUnit.MILLISECONDS, r.getDelay())));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.test.mockserver;
|
||||
|
||||
/**
|
||||
* Creates objects using standard data formats. Despite the format, all objects include a recognizable identifier in the
|
||||
* form: coreutils -> objectId -> <tt>Integer</tt>. All objects are valid, well-formed documents that can be parsed
|
||||
* with standard tools.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
* @see <a href="http://json.org/example.html">JSON Example</a>
|
||||
*/
|
||||
public final class ObjectResponseFactory {
|
||||
|
||||
/**
|
||||
* Creates a JSON formatted string with object Id 1.
|
||||
* @return JSON formatted string with object Id 1.
|
||||
*/
|
||||
public static String jsonObject1() {
|
||||
return new StringBuilder()
|
||||
.append("{")
|
||||
.append("\"glossary\": {")
|
||||
.append("\"coreutils\": {")
|
||||
.append("\"objectId\": \"1\"")
|
||||
.append("},")
|
||||
.append("\"title\": \"example glossary\",")
|
||||
.append("\"GlossDiv\": {")
|
||||
.append("\"title\": \"S\",")
|
||||
.append("\"GlossList\": {")
|
||||
.append("\"GlossEntry\": {")
|
||||
.append("\"ID\": \"SGML\",")
|
||||
.append("\"SortAs\": \"SGML\",")
|
||||
.append("\"GlossTerm\": \"Standard Generalized Markup Language\",")
|
||||
.append("\"Acronym\": \"SGML\",")
|
||||
.append("\"Abbrev\": \"ISO 8879:1986\",")
|
||||
.append("\"GlossDef\": {")
|
||||
.append("\"para\": \"A meta-markup language, used to create markup languages such as DocBook.\",")
|
||||
.append("\"GlossSeeAlso\": [\"GML\", \"XML\"]")
|
||||
.append("},")
|
||||
.append("\"GlossSee\": \"markup\"")
|
||||
.append("}")
|
||||
.append("}")
|
||||
.append("}")
|
||||
.append("}")
|
||||
.append("}")
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON formatted string with object Id 2.
|
||||
* @return JSON formatted string with object Id 2.
|
||||
*/
|
||||
public static String jsonObject2() {
|
||||
return new StringBuilder()
|
||||
.append("{\"menu\": {")
|
||||
.append("\"coreutils\": {")
|
||||
.append("\"objectId\": \"2\"")
|
||||
.append("},")
|
||||
.append("\"header\": \"SVG Viewer\",")
|
||||
.append("\"items\": [")
|
||||
.append("{\"id\": \"Open\"},")
|
||||
.append("{\"id\": \"OpenNew\", \"label\": \"Open New\"},")
|
||||
.append("null,")
|
||||
.append("{\"id\": \"ZoomIn\", \"label\": \"Zoom In\"},")
|
||||
.append("{\"id\": \"ZoomOut\", \"label\": \"Zoom Out\"},")
|
||||
.append("{\"id\": \"OriginalView\", \"label\": \"Original View\"},")
|
||||
.append("null,")
|
||||
.append("{\"id\": \"Quality\"},")
|
||||
.append("{\"id\": \"Pause\"},")
|
||||
.append("{\"id\": \"Mute\"},")
|
||||
.append("null,")
|
||||
.append("{\"id\": \"Find\", \"label\": \"Find...\"},")
|
||||
.append("{\"id\": \"FindAgain\", \"label\": \"Find Again\"},")
|
||||
.append("{\"id\": \"Copy\"},")
|
||||
.append("{\"id\": \"CopyAgain\", \"label\": \"Copy Again\"},")
|
||||
.append("{\"id\": \"CopySVG\", \"label\": \"Copy SVG\"},")
|
||||
.append("{\"id\": \"ViewSVG\", \"label\": \"View SVG\"},")
|
||||
.append("{\"id\": \"ViewSource\", \"label\": \"View Source\"},")
|
||||
.append("{\"id\": \"SaveAs\", \"label\": \"Save As\"},")
|
||||
.append("null,")
|
||||
.append("{\"id\": \"Help\"},")
|
||||
.append("{\"id\": \"About\", \"label\": \"About Adobe CVG Viewer...\"}")
|
||||
.append("]")
|
||||
.append("}}")
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a XML formatted string with object Id 1.
|
||||
* @return XML formatted string with object Id 1.
|
||||
*/
|
||||
public static String xmlObject1() {
|
||||
return new StringBuilder()
|
||||
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
|
||||
.append("<glossary><title>example glossary</title>")
|
||||
.append("<coreutils><objectId>1</objectId></coreutils>")
|
||||
.append("<GlossDiv><title>S</title>")
|
||||
.append("<GlossList>")
|
||||
.append("<GlossEntry ID=\"SGML\" SortAs=\"SGML\">")
|
||||
.append("<GlossTerm>Standard Generalized Markup Language</GlossTerm>")
|
||||
.append("<Acronym>SGML</Acronym>")
|
||||
.append("<Abbrev>ISO 8879:1986</Abbrev>")
|
||||
.append("<GlossDef>")
|
||||
.append("<para>A meta-markup language, used to create markup")
|
||||
.append("languages such as DocBook.</para>")
|
||||
.append("<GlossSeeAlso OtherTerm=\"GML\" />")
|
||||
.append("<GlossSeeAlso OtherTerm=\"XML\" />")
|
||||
.append("</GlossDef>")
|
||||
.append("<GlossSee OtherTerm=\"markup\" />")
|
||||
.append("</GlossEntry>")
|
||||
.append("</GlossList>")
|
||||
.append("</GlossDiv>")
|
||||
.append("</glossary>")
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a XML formatted string with object Id 2.
|
||||
* @return XML formatted string with object Id 2.
|
||||
*/
|
||||
public static String xmlObject2() {
|
||||
return new StringBuilder()
|
||||
.append("<menu>")
|
||||
.append("<coreutils><objectId>2</objectId></coreutils>")
|
||||
.append("<header>Adobe SVG Viewer</header>")
|
||||
.append("<item action=\"Open\" id=\"Open\">Open</item>")
|
||||
.append("<item action=\"OpenNew\" id=\"OpenNew\">Open New</item>")
|
||||
.append("<separator/>")
|
||||
.append("<item action=\"ZoomIn\" id=\"ZoomIn\">Zoom In</item>")
|
||||
.append("<item action=\"ZoomOut\" id=\"ZoomOut\">Zoom Out</item>")
|
||||
.append("<item action=\"OriginalView\" id=\"OriginalView\">Original View</item>")
|
||||
.append("<separator/>")
|
||||
.append("<item action=\"Quality\" id=\"Quality\">Quality</item>")
|
||||
.append("<item action=\"Pause\" id=\"Pause\">Pause</item>")
|
||||
.append("<item action=\"Mute\" id=\"Mute\">Mute</item>")
|
||||
.append("<separator/>")
|
||||
.append("<item action=\"Find\" id=\"Find\">Find...</item>")
|
||||
.append("<item action=\"FindAgain\" id=\"FindAgain\">Find Again</item>")
|
||||
.append("<item action=\"Copy\" id=\"Copy\">Copy</item>")
|
||||
.append("<item action=\"CopyAgain\" id=\"CopyAgain\">Copy Again</item>")
|
||||
.append("<item action=\"CopySVG\" id=\"CopySVG\">Copy SVG</item>")
|
||||
.append("<item action=\"ViewSVG\" id=\"ViewSVG\">View SVG</item>")
|
||||
.append("<item action=\"ViewSource\" id=\"ViewSource\">View Source</item>")
|
||||
.append("<item action=\"SaveAs\" id=\"SaveAs\">Save As</item>")
|
||||
.append("<separator/>")
|
||||
.append("<item action=\"Help\" id=\"Help\">Help</item>")
|
||||
.append("<item action=\"About\" id=\"About\">About Adobe CVG Viewer...</item>")
|
||||
.append("</menu>")
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Core Utils - Fiber-enabled clients.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.fiber.test.mockserver;
|
||||
|
||||
import static java.nio.file.Files.readAllBytes;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static javax.xml.xpath.XPathConstants.STRING;
|
||||
import static org.apache.commons.lang3.StringUtils.trimToNull;
|
||||
import static org.hamcrest.CoreMatchers.allOf;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathExpression;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Validates object responses.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public final class ObjectResponseValidator {
|
||||
|
||||
public static boolean isValidXml(final String payload, final String expectedId) {
|
||||
boolean isValid = false;
|
||||
try {
|
||||
final String objectId = readObjectIdFromXml(payload);
|
||||
isValid = expectedId.equals(trimToNull(objectId));
|
||||
} catch (Exception e) {
|
||||
isValid = false;
|
||||
e.printStackTrace();
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public static void validateXml(final File file, final String expectedId) throws SAXException, IOException, ParserConfigurationException, XPathExpressionException {
|
||||
final String objectId = readObjectIdFromXml(new String(readAllBytes(file.toPath())));
|
||||
assertThat("Object Id coincides with expected", objectId, allOf(notNullValue(), not(equalTo("")), equalTo(expectedId)));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String readObjectIdFromXml(final String payload) throws SAXException, IOException, ParserConfigurationException, XPathExpressionException {
|
||||
final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(payload.getBytes()));
|
||||
final XPath xPath = XPathFactory.newInstance().newXPath();
|
||||
final XPathExpression xPathExpression = xPath.compile("//*/coreutils/objectId/text()");
|
||||
return trimToNull((String) xPathExpression.evaluate(document, STRING));
|
||||
}
|
||||
|
||||
public static boolean isValidJson(final String payload, final String expectedId) {
|
||||
boolean isValid = false;
|
||||
try {
|
||||
final String objectId = readObjectIdFromJson(payload);
|
||||
isValid = expectedId.equals(trimToNull(objectId));
|
||||
} catch (Exception e) {
|
||||
isValid = false;
|
||||
e.printStackTrace();
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public static void validateJson(final File file, final String expectedId) throws IOException, JSONException {
|
||||
final String objectId = readObjectIdFromJson(new String(readAllBytes(file.toPath())));
|
||||
assertThat("Object Id coincides with expected", objectId, allOf(notNullValue(), not(equalTo("")), equalTo(expectedId)));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String readObjectIdFromJson(final String payload) throws JSONException {
|
||||
String objectId = null;
|
||||
final JSONObject rootElem = new JSONObject(payload);
|
||||
final String[] names = JSONObject.getNames(rootElem);
|
||||
if (names != null) {
|
||||
boolean found = false;
|
||||
for (int i = 0; i < names.length && !found; i++) {
|
||||
final JSONObject childElem = rootElem.getJSONObject(names[i]);
|
||||
if (Arrays.asList(ofNullable(JSONObject.getNames(childElem)).orElse(new String[]{})).contains("coreutils")) {
|
||||
objectId = trimToNull(childElem.getJSONObject("coreutils").getString("objectId"));
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return objectId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Core Utils - Fiber-enabled clients.
|
||||
Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
|
||||
This product combines work with different licenses. See the "NOTICE" text
|
||||
file for details on the various modules and licenses.
|
||||
|
||||
The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
-->
|
||||
<configuration debug="false">
|
||||
|
||||
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
|
||||
<resetJUL>true</resetJUL>
|
||||
</contextListener>
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>${java.io.tmpdir}/coreutils-fiber-test.log</file>
|
||||
<append>false</append>
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.grycap.coreutils" level="TRACE" />
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Core Utils - Fiber-enabled clients.
|
||||
Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
|
||||
This product combines work with different licenses. See the "NOTICE" text
|
||||
file for details on the various modules and licenses.
|
||||
|
||||
The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
-->
|
||||
<configuration debug="false">
|
||||
|
||||
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
|
||||
<resetJUL>true</resetJUL>
|
||||
</contextListener>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="OFF">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
# Created by https://www.gitignore.io/api/eclipse,java,maven
|
||||
|
||||
### Eclipse ###
|
||||
*.pydevproject
|
||||
.metadata
|
||||
.gradle
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
|
||||
### Java ###
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
|
||||
### Maven ###
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Core Utils - Common Utilities.
|
||||
Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
|
||||
This product combines work with different licenses. See the "NOTICE" text
|
||||
file for details on the various modules and licenses.
|
||||
|
||||
The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.grycap.coreutils</groupId>
|
||||
<artifactId>coreutils</artifactId>
|
||||
<version>0.1.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>coreutils-logging</artifactId>
|
||||
<name>Common logging utilities</name>
|
||||
<description>Provides utilities to simplify logging</description>
|
||||
<url>https://github.com/grycap/coreutils</url>
|
||||
|
||||
<dependencies>
|
||||
<!-- GRyCAP common utilities -->
|
||||
<dependency>
|
||||
<groupId>org.grycap.coreutils</groupId>
|
||||
<artifactId>coreutils-common</artifactId>
|
||||
<version>${org.grycap.coreutils.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.grycap.coreutils</groupId>
|
||||
<artifactId>coreutils-test</artifactId>
|
||||
<version>${org.grycap.coreutils.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Logback and SLF4J logging framework -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Core Utils - Logging utilities.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.logging;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* Wraps the SLF4J {@link org.slf4j.LoggerFactory}
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public final class ExtendedLoggerFactory {
|
||||
|
||||
/**
|
||||
* Uses the method {@link org.slf4j.LoggerFactory#getLogger(Class)} to create a logger named corresponding to the
|
||||
* class passed as parameter. In addition, this method will configure the logging system, installing the necessary
|
||||
* bridges to unify logging across the entire application.
|
||||
* @param clazz - the returned logger will be named after clazz
|
||||
* @return A logger named corresponding to the class passed as parameter.
|
||||
*/
|
||||
public static Logger getLogger(final Class<?> clazz) {
|
||||
org.grycap.coreutils.logging.LogManager.getLogManager().init();
|
||||
return org.slf4j.LoggerFactory.getLogger(requireNonNull(clazz, "A non-null class expected"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Core Utils - Logging utilities.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.logging;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
import static org.slf4j.bridge.SLF4JBridgeHandler.install;
|
||||
import static org.slf4j.bridge.SLF4JBridgeHandler.removeHandlersForRootLogger;
|
||||
import static org.slf4j.bridge.SLF4JBridgeHandler.uninstall;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
* Manages loggers, installing the necessary bridges to unify logging across an entire library, service or application.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class LogManager {
|
||||
|
||||
private final static Logger LOGGER = getLogger(LogManager.class);
|
||||
|
||||
private final static AtomicBoolean PRISTINE = new AtomicBoolean(true);
|
||||
|
||||
private LogManager() { }
|
||||
|
||||
public static LogManager getLogManager() {
|
||||
return new LogManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* The logging system is automatically configured when the method {@link LogManager#getLogger(Class)} is called.
|
||||
* This method is conveniently provided to initialize the logging system before creating any logger with this class.
|
||||
* Also, you can call this method to initialize the logging system without modifying your code (creating loggers
|
||||
* with SLF4J or other logging framework).
|
||||
*/
|
||||
public void init() {
|
||||
if (PRISTINE.getAndSet(false)) {
|
||||
// removes/unregisters/detaches all handlers currently attached to the root logger
|
||||
removeHandlersForRootLogger();
|
||||
// add SLF4JBridgeHandler to j.u.l's root logger (should be done once during the initialization phase of the application)
|
||||
install();
|
||||
LOGGER.info("Log manager was loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall the logging bridges returning to the original configuration provided by the individual logging frameworks.
|
||||
*/
|
||||
public void reset() {
|
||||
LOGGER.info("About to reset log manager: no more coherent log messages guaranteed beyond this point.");
|
||||
// uninstall the SLF4JBridgeHandler instance attached to the jul's root logger
|
||||
uninstall();
|
||||
// removes/unregisters/detaches all handlers currently attached to the root logger
|
||||
removeHandlersForRootLogger();
|
||||
}
|
||||
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Core Utils - Logging utilities.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.logging.test;
|
||||
|
||||
import static org.apache.commons.io.FileUtils.deleteQuietly;
|
||||
import static org.grycap.coreutils.logging.LogManager.getLogManager;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.grycap.coreutils.test.category.FunctionalTests;
|
||||
import org.grycap.coreutils.test.rules.TestPrinter;
|
||||
import org.grycap.coreutils.test.rules.TestWatcher2;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.core.util.StatusPrinter;
|
||||
|
||||
/**
|
||||
* Tests the log manager.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
@Category(FunctionalTests.class)
|
||||
public class LogManagerTest {
|
||||
|
||||
private static final String TESTLOG_FILENAME = new StringBuilder(System.getProperty("java.io.tmpdir")).append(System.getProperty("file.separator"))
|
||||
.append("coreutils-logging-test.log").toString();
|
||||
|
||||
@Rule
|
||||
public TestPrinter pw = new TestPrinter();
|
||||
|
||||
@Rule
|
||||
public TestRule watchman = new TestWatcher2(pw);
|
||||
|
||||
@BeforeClass
|
||||
public static void cleanup() {
|
||||
deleteQuietly(new File(TESTLOG_FILENAME));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void reset() {
|
||||
getLogManager().reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactory() throws Exception {
|
||||
// setup logging via the factory method that internally calls the initialization method
|
||||
final Logger logger = org.grycap.coreutils.logging.ExtendedLoggerFactory.getLogger(DummyClass.class);
|
||||
assertThat("Logger is not null", logger, notNullValue());
|
||||
|
||||
// j.u.l. logger
|
||||
String msg = "This message was generated with j.u.l., logged with Logback+SL4J";
|
||||
final java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger(LogManagerTest.class.getCanonicalName());
|
||||
julLogger.setLevel(java.util.logging.Level.INFO);
|
||||
julLogger.info(msg);
|
||||
assertThatLogfileContains(msg);
|
||||
|
||||
// log4j logger
|
||||
msg = "This message was generated with log4j, logged with Logback+SL4J";
|
||||
final org.apache.log4j.Logger log4jLogger = org.apache.log4j.Logger.getLogger(LogManagerTest.class);
|
||||
log4jLogger.setLevel(org.apache.log4j.Level.INFO);
|
||||
log4jLogger.info(msg);
|
||||
assertThatLogfileContains(msg);
|
||||
|
||||
// SLF4J logger
|
||||
msg = "This message was generated with SL4J, logged with Logback+SL4J";
|
||||
final org.slf4j.Logger slf4jLogger = org.slf4j.LoggerFactory.getLogger(LogManagerTest.class);
|
||||
slf4jLogger.info(msg);
|
||||
assertThatLogfileContains(msg);
|
||||
|
||||
// print logback internal state
|
||||
if (pw.isPrintEnabled()) {
|
||||
final LoggerContext loggerContext = (LoggerContext) org.slf4j.LoggerFactory.getILoggerFactory();
|
||||
StatusPrinter.print(loggerContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertThatLogfileContains(final String msg) throws IOException {
|
||||
assertThat("Log file contains expected message", Files.lines(Paths.get(TESTLOG_FILENAME)).anyMatch(line -> line.contains(msg)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy class.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public static class DummyClass { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Core Utils - Common Utilities.
|
||||
Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
|
||||
This product combines work with different licenses. See the "NOTICE" text
|
||||
file for details on the various modules and licenses.
|
||||
|
||||
The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
-->
|
||||
<configuration debug="false">
|
||||
|
||||
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
|
||||
<resetJUL>true</resetJUL>
|
||||
</contextListener>
|
||||
|
||||
<!-- <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender> -->
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>${java.io.tmpdir}/coreutils-logging-test.log</file>
|
||||
<append>false</append>
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.grycap.coreutils" level="TRACE" />
|
||||
|
||||
<root level="INFO">
|
||||
<!-- <appender-ref ref="STDOUT" /> -->
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
# Created by https://www.gitignore.io/api/eclipse,java,maven
|
||||
|
||||
### Eclipse ###
|
||||
*.pydevproject
|
||||
.metadata
|
||||
.gradle
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
|
||||
### Java ###
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
|
||||
### Maven ###
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Core Utils - Testing Utilities.
|
||||
Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
|
||||
This product combines work with different licenses. See the "NOTICE" text
|
||||
file for details on the various modules and licenses.
|
||||
|
||||
The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.grycap.coreutils</groupId>
|
||||
<artifactId>coreutils</artifactId>
|
||||
<version>0.1.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>coreutils-test</artifactId>
|
||||
<name>Common testing utilities</name>
|
||||
<description>Provides utilities to simplify common testing tasks</description>
|
||||
<url>https://github.com/grycap/coreutils</url>
|
||||
|
||||
<dependencies>
|
||||
<!-- JSR-305: Annotations for Software Defect Detection -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Google Gson library -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- JUnit -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--Extends the Hamcrest core matcher that is transitively included with JUnit -->
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-library</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- A toolkit for testing multi-threaded code -->
|
||||
<dependency>
|
||||
<groupId>net.jodah</groupId>
|
||||
<artifactId>concurrentunit</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Core Utils - Testing utilities.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.test.category;
|
||||
|
||||
/**
|
||||
* Categorize unit tests that belongs to the functional testing group.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public interface FunctionalTests { }
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Core Utils - Testing utilities.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.test.category;
|
||||
|
||||
/**
|
||||
* Categorize unit tests that belongs to the integration testing group.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public interface IntegrationTests { }
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Core Utils - Testing utilities.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.test.category;
|
||||
|
||||
/**
|
||||
* Categorize unit tests that belongs to the sanity testing group.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public interface SanityTests { }
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Core Utils - Testing utilities.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.test.rules;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.junit.rules.ExternalResource;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
/**
|
||||
* Provides common methods for printing objects during tests execution. This class can be configured to silently ignore
|
||||
* print operations, producing no output.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class TestPrinter extends ExternalResource {
|
||||
|
||||
private final boolean print;
|
||||
|
||||
/**
|
||||
* Default constructor gets its configuration from the system environment.
|
||||
*/
|
||||
public TestPrinter() {
|
||||
this(System.getProperty("grycap.tests.print.out", "false").equals("true"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor that receives configuration parameters during class creation.
|
||||
* @param print - flag to enable/disable message printing to the STDOUT
|
||||
*/
|
||||
public TestPrinter(final boolean print) {
|
||||
this.print = print;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the printer is configured to print the messages or not.
|
||||
* @return <tt>true</tt> when the printer is configured to print the messages, otherwise <tt>false</tt>.
|
||||
*/
|
||||
public boolean isPrintEnabled() {
|
||||
return print;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a new line character.
|
||||
*/
|
||||
public void println() {
|
||||
println("", OutputFormat.PLAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a {@link String} representation of the provided object by calling the {@link Object#toString()} method and
|
||||
* print its content to the standard output.
|
||||
* @param obj - object that will be printed
|
||||
*/
|
||||
public void println(final @Nullable Object obj) {
|
||||
println(obj, OutputFormat.PLAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a {@link String} to the standard output.
|
||||
* @param msg - message that will be printed
|
||||
*/
|
||||
public void println(final String msg) {
|
||||
println(msg, OutputFormat.PLAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a JSON representation of the provided {@link String} and prints its content to the standard output.
|
||||
* @param msg - message that will be printed
|
||||
*/
|
||||
public void printlnJson(final String msg) {
|
||||
println(msg, OutputFormat.JSON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the provided object to one of the supported data formats and print the object's content to the standard output.
|
||||
* @param obj - object that will be printed
|
||||
* @param format - data format that will be used to print the object
|
||||
*/
|
||||
private void println(final @Nullable Object obj, final @Nullable OutputFormat format) {
|
||||
switch (ofNullable(format).orElse(OutputFormat.PLAIN)) {
|
||||
case JSON:
|
||||
if (print) System.out.println(GsonHelper.gson.toJson(obj));
|
||||
break;
|
||||
case PLAIN:
|
||||
default:
|
||||
if (print) System.out.println(obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported output formats.
|
||||
* @author Erik Torres <ertorser@upv.es>
|
||||
*/
|
||||
public enum OutputFormat {
|
||||
JSON,
|
||||
PLAIN
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy initialization of Gson-based JSON processor.
|
||||
* @author Erik Torres <ertorser@upv.es>
|
||||
*/
|
||||
private static class GsonHelper {
|
||||
public static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Core Utils - Testing utilities.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.test.rules;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.junit.AssumptionViolatedException;
|
||||
import org.junit.rules.TestWatcher;
|
||||
import org.junit.runner.Description;
|
||||
|
||||
/**
|
||||
* Keeps a log of each passing and failing test.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class TestWatcher2 extends TestWatcher {
|
||||
|
||||
/**
|
||||
* Default header that will be printed before the status messages.
|
||||
*/
|
||||
private static final String DEFAULT_HEADER = " >>> TestUtils >>> ";
|
||||
|
||||
/**
|
||||
* Default footer that will be printed after the status messages.
|
||||
*/
|
||||
private static final String DEFAULT_FOOTER = "";
|
||||
|
||||
private final TestPrinter pw;
|
||||
private final String header;
|
||||
private final String footer;
|
||||
|
||||
/**
|
||||
* Convenient constructor that configures the class to use the default header and footer.
|
||||
* @param pw - the printer that will be used to print the status messages
|
||||
*/
|
||||
public TestWatcher2(final TestPrinter pw) {
|
||||
this(pw, DEFAULT_HEADER, DEFAULT_FOOTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor that receives initial values of the class attributes as parameters.
|
||||
* @param pw - the printer that will be used to print the status messages
|
||||
* @param header - optional header that will be printed before the status messages or {@link #DEFAULT_HEADER}
|
||||
* when a <tt>null</tt> value is passed
|
||||
* @param footer - optional footer that will be printed after the status messages or {@link #DEFAULT_FOOTER}
|
||||
* when a <tt>null</tt> value is passed
|
||||
*/
|
||||
public TestWatcher2(final TestPrinter pw, final @Nullable String header, final @Nullable String footer) {
|
||||
this.pw = pw;
|
||||
this.header = ofNullable(header).orElse(DEFAULT_HEADER);
|
||||
this.footer = ofNullable(footer).orElse(DEFAULT_FOOTER);;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void succeeded(final Description description) {
|
||||
pw.println(new StringBuffer(header).append(description.getDisplayName()).append(" ").append("succeeded!").append(footer).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void failed(final Throwable e, final Description description) {
|
||||
pw.println(new StringBuffer(header).append(description.getDisplayName()).append(" ").append("failed!").append(footer).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void skipped(final AssumptionViolatedException e, final Description description) {
|
||||
pw.println(new StringBuffer(header).append(description.getDisplayName()).append(" ").append("skipped!").append(footer).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void starting(final Description description) {
|
||||
pw.println(new StringBuffer(header).append(description.getDisplayName()).append(" ").append("starting...").append(footer).toString());
|
||||
}
|
||||
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Core Utils - Testing utilities.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.test.util;
|
||||
|
||||
import static java.io.File.separator;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Utilities to load test resources from class-path and file-system.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class ResourceLoadingUtils {
|
||||
|
||||
/**
|
||||
* Test resource directory is scanned for this file in order to find the directory where the application made available the
|
||||
* files that contain test cases to validate the different test scenarios. A directory named {@link #TEST_DIRNAME} should be
|
||||
* created at the same level of this file and the test files should be placed there.
|
||||
*/
|
||||
public static final String ANCHOR_FILENAME = "grycap-testfiles-m2";
|
||||
|
||||
/**
|
||||
* Name of the directory where the test files are located. Files can be organized as needed inside this directory (e.g. using
|
||||
* subdirectories).
|
||||
*/
|
||||
public static final String TEST_DIRNAME = "testfiles";
|
||||
|
||||
/**
|
||||
* Finds the directory where the test files are located.
|
||||
*/
|
||||
public static final String TEST_RESOURCES_DIR;
|
||||
static {
|
||||
String candidateDir = null;
|
||||
File anchorFile = null;
|
||||
try {
|
||||
anchorFile = new File(ResourceLoadingUtils.class.getClassLoader().getResource(ANCHOR_FILENAME).toURI());
|
||||
} catch (Exception e) {
|
||||
anchorFile = new File(System.getProperty("user.dir"));
|
||||
} finally {
|
||||
final File resDir = new File(new StringBuffer(anchorFile.getParent()).append(separator).append(TEST_DIRNAME).toString());
|
||||
if (resDir != null && resDir.isDirectory() && resDir.canRead()) {
|
||||
try {
|
||||
candidateDir = resDir.getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_RESOURCES_DIR = candidateDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the resources with filenames that end with one of the provided extensions. For each matching resource
|
||||
* the path name is provided. Files are not created to prevent the limits that OS could impose on the simultaneous
|
||||
* number of opened files.
|
||||
* @param dirname - name of the target directory
|
||||
* @param extensions - valid file extensions
|
||||
* @return The resources with filenames that end with one of the provided extensions.
|
||||
*/
|
||||
public static List<String> getResourceFiles(final String dirname, final @Nullable String[] extensions) {
|
||||
checkResourcesDir();
|
||||
final File dir = new File(new StringBuffer(TEST_RESOURCES_DIR).append(separator).append(dirname).toString());
|
||||
return asList(ofNullable(dir.listFiles(new ExtensionFilter(extensions))).orElse(new File[]{})).stream()
|
||||
.map(File::getAbsolutePath)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static void checkResourcesDir() {
|
||||
if (TEST_RESOURCES_DIR == null) {
|
||||
throw new IllegalStateException("Invalid test resources pathname: " + TEST_RESOURCES_DIR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A filename filter that matches the files by their extension.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public static class ExtensionFilter implements FilenameFilter {
|
||||
|
||||
private final List<String> extensions;
|
||||
|
||||
/**
|
||||
* Convenient constructor to initialize the valid file extensions from an array.
|
||||
* @param extensions - valid file extensions
|
||||
*/
|
||||
public ExtensionFilter(final @Nullable String[] extensions) {
|
||||
this(asList(ofNullable(extensions).orElse(new String[]{})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor to initialize the valid file extensions from a list.
|
||||
* @param extensions - valid file extensions
|
||||
*/
|
||||
public ExtensionFilter(final @Nullable List<String> extensions) {
|
||||
this.extensions = ofNullable(extensions).orElse(new ArrayList<>()).stream()
|
||||
.map(e -> e != null ? e.trim().toLowerCase() : null)
|
||||
.filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(final File file, final String name) {
|
||||
final String name2 = (name != null ? name.trim().toLowerCase() : null);
|
||||
if (name2 == null) return false;
|
||||
return extensions.stream().anyMatch(e -> name2.endsWith(e));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Core Utils - Testing utilities.
|
||||
* Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
* This product combines work with different licenses. See the "NOTICE" text
|
||||
* file for details on the various modules and licenses.
|
||||
*
|
||||
* The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
* that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
*/
|
||||
|
||||
package org.grycap.coreutils.test;
|
||||
|
||||
import static java.io.File.separator;
|
||||
import static org.grycap.coreutils.test.util.ResourceLoadingUtils.getResourceFiles;
|
||||
import static org.hamcrest.CoreMatchers.allOf;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
|
||||
import static org.hamcrest.collection.IsEmptyCollection.empty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.grycap.coreutils.test.category.FunctionalTests;
|
||||
import org.grycap.coreutils.test.rules.TestPrinter;
|
||||
import org.grycap.coreutils.test.rules.TestWatcher2;
|
||||
import org.grycap.coreutils.test.util.ResourceLoadingUtils;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.junit.rules.TestRule;
|
||||
|
||||
/**
|
||||
* Tests resource loading utilities provided by {@link ResourceLoadingUtils}.
|
||||
* @author Erik Torres <etserrano@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
@Category(FunctionalTests.class)
|
||||
public class ResourceLoadingTest {
|
||||
|
||||
@Rule
|
||||
public TestPrinter pw = new TestPrinter();
|
||||
|
||||
@Rule
|
||||
public TestRule watchman = new TestWatcher2(pw);
|
||||
|
||||
@Test
|
||||
public void testSimpleDirectoryHierarchy() {
|
||||
// test simple directory hierarchy
|
||||
final List<String> files = getResourceFiles("examples", new String[]{ ".txt", ".log" });
|
||||
assertThat("list of files coincides with expected", files, allOf(notNullValue(), hasSize(3)));
|
||||
final String filenames = files.stream().map(s -> s + "\n").reduce("", String::concat);
|
||||
assertThat("all expected files are included", filenames, allOf(containsString("example1.txt"),
|
||||
containsString("example2.txt"), containsString("example4.log")));
|
||||
pw.println("\n >> Files: " + files);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedDirectoryHierarchy() {
|
||||
// test complex directory hierarchy
|
||||
final List<String> files = getResourceFiles(new StringBuffer("examples").append(separator)
|
||||
.append("inner_examples").toString(), new String[]{ ".txt" });
|
||||
assertThat("list of files coincides with expected", files, allOf(notNullValue(), hasSize(1)));
|
||||
final String filenames = files.stream().map(s -> s + "\n").reduce("", String::concat);
|
||||
assertThat("all expected files are included", filenames, containsString("example.txt"));
|
||||
pw.println("\n >> Files: " + files);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidDirectory() {
|
||||
// test empty directory
|
||||
final List<String> files = getResourceFiles("i_dont_exist", new String[]{ ".txt" });
|
||||
assertThat("list of files coincides with expected", files, allOf(notNullValue(), empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyExtensionList() {
|
||||
// test empty extension list
|
||||
final List<String> files = getResourceFiles("examples", null);
|
||||
assertThat("list of files coincides with expected", files, allOf(notNullValue(), empty()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Core Utils - Parent Project.
|
||||
Copyright 2015-2016 GRyCAP (Universitat Politecnica de Valencia)
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
|
||||
This product combines work with different licenses. See the "NOTICE" text
|
||||
file for details on the various modules and licenses.
|
||||
|
||||
The "NOTICE" text file is part of the distribution. Any derivative works
|
||||
that you distribute must include a readable copy of the "NOTICE" text file.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.grycap.coreutils</groupId>
|
||||
<artifactId>coreutils</artifactId>
|
||||
<version>0.1.0</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Core utils project</name>
|
||||
<description>Core utils parent pom</description>
|
||||
<url>https://github.com/grycap/coreutils</url>
|
||||
|
||||
<inceptionYear>2015</inceptionYear>
|
||||
|
||||
<organization>
|
||||
<name>GRyCAP</name>
|
||||
<url>http://www.grycap.upv.es/</url>
|
||||
</organization>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/grycap/coreutils</url>
|
||||
<connection>scm:git:git://github.com/grycap/coreutils.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:grycap/coreutils.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<email>etserrano@gmail.com</email>
|
||||
<name>Erik Torres</name>
|
||||
<url>https://github.com/etorres</url>
|
||||
<id>etorres</id>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
|
||||
<!-- Convenience properties to set library versions -->
|
||||
<ch.qos.logback.version>1.1.3</ch.qos.logback.version>
|
||||
<junit.version>4.12</junit.version> <!-- affects Hamcrest version -->
|
||||
<org.grycap.coreutils.version>${project.version}</org.grycap.coreutils.version>
|
||||
<org.hamcrest.version>1.3</org.hamcrest.version> <!-- affected by JUnit version -->
|
||||
<org.mock-server.version>3.10.2</org.mock-server.version>
|
||||
<org.slf4j.version>1.7.13</org.slf4j.version>
|
||||
|
||||
<!-- Convenience properties to set Maven plugins versions -->
|
||||
<m2e.lifecycle-mapping.version>1.0.0</m2e.lifecycle-mapping.version>
|
||||
<maven-antrun-plugin.version>1.8</maven-antrun-plugin.version>
|
||||
<maven-compiler-plugin.version>3.3</maven-compiler-plugin.version>
|
||||
<maven-dependency-plugin.version>2.10</maven-dependency-plugin.version>
|
||||
<maven-failsafe-plugin.version>2.19</maven-failsafe-plugin.version>
|
||||
<maven-surefire-plugin.version>2.19</maven-surefire-plugin.version>
|
||||
<mockserver-maven-plugin.version>3.10.2</mockserver-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- Apache Commons: Collection of open source reusable Java components -->
|
||||
<dependency> <!-- Utility classes for the classes that are considered to be Java standards -->
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.4</version>
|
||||
</dependency>
|
||||
<dependency> <!-- I/O utilities -->
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.4</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JSR-305 annotations for Software Defect Detection -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Google Gson library -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Configuration library -->
|
||||
<dependency>
|
||||
<groupId>com.typesafe</groupId>
|
||||
<artifactId>config</artifactId>
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Integrates with Quasar fibers via the Comsat APIs -->
|
||||
<dependency>
|
||||
<groupId>co.paralleluniverse</groupId>
|
||||
<artifactId>quasar-core</artifactId>
|
||||
<version>0.7.3</version>
|
||||
<classifier>jdk8</classifier>
|
||||
</dependency>
|
||||
<dependency> <!-- Apache HTTP client -->
|
||||
<groupId>co.paralleluniverse</groupId>
|
||||
<artifactId>comsat-httpclient</artifactId>
|
||||
<version>0.5.0</version>
|
||||
</dependency>
|
||||
<dependency> <!-- Modern HTTP+SPDY client -->
|
||||
<groupId>co.paralleluniverse</groupId>
|
||||
<artifactId>comsat-okhttp</artifactId>
|
||||
<version>0.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Logback logging framework -->
|
||||
<dependency> <!-- classic module (uses the SLF4J API) -->
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${ch.qos.logback.version}</version>
|
||||
<exclusions>
|
||||
<exclusion> <!-- use the SLF4J version included with the bridges -->
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- SLF4J logging framework -->
|
||||
<dependency> <!-- java.util.logging (jul) bridge -->
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
<version>${org.slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency> <!-- Log4j implemented over SLF4J -->
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
<version>${org.slf4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JUnit unit testing framework for Java -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Hamcrest library of matcher implementations -->
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-library</artifactId>
|
||||
<version>${org.hamcrest.version}</version>
|
||||
<exclusions>
|
||||
<exclusion> <!-- Exclude Hamcrest core, which is transitively included with JUnit -->
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- A toolkit for testing multi-threaded code -->
|
||||
<dependency>
|
||||
<groupId>net.jodah</groupId>
|
||||
<artifactId>concurrentunit</artifactId>
|
||||
<version>0.4.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MockServer -->
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-integration-testing</artifactId>
|
||||
<version>${org.mock-server.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!-- Maven Compiler Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
</plugin>
|
||||
<!-- MockServer Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-maven-plugin</artifactId>
|
||||
<version>${mockserver-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<serverPort>9080</serverPort>
|
||||
<logLevel>WARN</logLevel>
|
||||
<pipeLogToConsole>true</pipeLogToConsole>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>pre-integration-test</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>start</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>post-integration-test</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>stop</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-integration-testing</artifactId>
|
||||
<version>${org.mock-server.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<!-- Maven Surefire Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
<dependencies>
|
||||
<dependency> <!-- SureFire JUnitCore (JUnit 4.7+) Runner -->
|
||||
<groupId>org.apache.maven.surefire</groupId>
|
||||
<artifactId>surefire-junit47</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
<excludedGroups>org.grycap.coreutils.test.category.IntegrationTests</excludedGroups>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Maven Failsafe Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>${maven-failsafe-plugin.version}</version>
|
||||
<dependencies>
|
||||
<dependency> <!-- SureFire JUnitCore (JUnit 4.7+) Runner -->
|
||||
<groupId>org.apache.maven.surefire</groupId>
|
||||
<artifactId>surefire-junit47</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>integration-test</id>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
<groups>org.grycap.coreutils.test.category.IntegrationTests</groups>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Cobertura: creates code coverage reports -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.7</version>
|
||||
<configuration>
|
||||
<format>xml</format>
|
||||
<maxmem>256m</maxmem>
|
||||
<aggregate>true</aggregate>
|
||||
<quiet>true</quiet>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<!-- Cobertura: creates code coverage reports -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- Submit code coverage reports to Coveralls web service -->
|
||||
<plugin>
|
||||
<groupId>org.eluder.coveralls</groupId>
|
||||
<artifactId>coveralls-maven-plugin</artifactId>
|
||||
<version>4.0.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<modules>
|
||||
<module>coreutils-test</module>
|
||||
<module>coreutils-common</module>
|
||||
<module>coreutils-logging</module>
|
||||
<module>coreutils-fiber</module>
|
||||
</modules>
|
||||
</project>
|
||||
Reference in New Issue
Block a user