Initial commit

This commit is contained in:
etorres
2015-12-19 13:04:16 +01:00
commit 664252827e
46 changed files with 4449 additions and 0 deletions
+74
View File
@@ -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
+202
View File
@@ -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.
+32
View File
@@ -0,0 +1,32 @@
# Core Utils
Utilities to facilitate Java applications development.
## Master Build Status
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/grycap/coreutils/blob/master/LICENSE)
[![Build Status](https://api.travis-ci.org/grycap/coreutils.svg)](https://travis-ci.org/grycap/coreutils/builds)
[![Coverage Status](https://coveralls.io/repos/grycap/coreutils/badge.svg?branch=master&service=github)](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``
+74
View File
@@ -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
+69
View File
@@ -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;
}
}
+74
View File
@@ -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
+276
View File
@@ -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;
}
}
@@ -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);
}
}
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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())));
});
}
}
@@ -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();
}
}
@@ -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>
+74
View File
@@ -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
+66
View File
@@ -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>
@@ -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();
}
}
@@ -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>
+74
View File
@@ -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
+70
View File
@@ -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());
}
}
@@ -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()));
}
}
+331
View File
@@ -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>