Add log api functionality
Change-Id: Ie7025de1bafaf788c8b5950c4a1ee6921b264d8b
This commit is contained in:
parent
257c20fa59
commit
c798c2cc54
|
@ -0,0 +1,31 @@
|
|||
*.py[co]
|
||||
*~
|
||||
doc/build/*
|
||||
dist
|
||||
build
|
||||
cover
|
||||
.coverage
|
||||
*.egg
|
||||
*.egg-info
|
||||
.testrepository
|
||||
.tox
|
||||
ChangeLog
|
||||
MANIFEST
|
||||
monasca.log
|
||||
|
||||
|
||||
*.swp
|
||||
*.iml
|
||||
.DS_Store
|
||||
.cache
|
||||
.classpath
|
||||
.idea
|
||||
.project
|
||||
.target/
|
||||
java/debs/*
|
||||
.settings/
|
||||
target
|
||||
test-output/
|
||||
logs/
|
||||
*config*.yml
|
||||
db/config.yml
|
|
@ -0,0 +1,175 @@
|
|||
|
||||
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.
|
|
@ -0,0 +1,78 @@
|
|||
# Forked from https://github.com/stackforge/monasca-api
|
||||
This repository is forked from [monasca-api](https://github.com/stackforge/monasca-api).
|
||||
|
||||
# Overview
|
||||
|
||||
`monasca-log-api` is a RESTful API server that is designed with a layered architecture [layered architecture](http://en.wikipedia.org/wiki/Multilayered_architecture).
|
||||
|
||||
The full API Specification can be found in [docs/monasca-log-api-spec.md](docs/monasca-log-api-spec.md)
|
||||
|
||||
## Java Build
|
||||
|
||||
Requires monasca-common from https://github.com/stackforge/monasca-common. Download and do mvn install. Then:
|
||||
|
||||
```
|
||||
cd java
|
||||
mvn clean package
|
||||
```
|
||||
|
||||
# StackForge Java Build
|
||||
|
||||
There is a pom.xml in the base directory that should only be used for the StackForge build. The StackForge build is a rather strange build because of the limitations of the current StackForge java jobs and infrastructure. We have found that the API runs faster if built with maven 3 but the StackForge nodes only have maven 2. This build checks the version of maven and if not maven 3, it downloads a version of maven 3 and uses it. This build depends on jars that are from monasca-common. That StrackForge build uploads the completed jars to http://tarballs.openstack.org/ci/monasca-common, but they are just regular jars, and not in a maven repository and sometimes zuul takes a long time to do the upload. Hence, the first thing the maven build from the base project does is invoke build_common.sh in the common directory. This script clones monasca-common and then invokes maven 3 to build monasca-common in the common directory and install the jars in the local maven repository.
|
||||
|
||||
Since this is all rather complex, that part of the build only works on StackForge so follow the simple instruction above if you are building your own monasca-log-api.
|
||||
|
||||
Currently this build is executed on the bare-precise nodes in StackForge and they only have maven 2. So, this build must be kept compatible with Maven 2. If another monasca-common jar is added as a dependency to java/pom.xml, it must also be added to download/download.sh.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
java -jar target/monasca-log-api.jar server config-file.yml
|
||||
```
|
||||
|
||||
## Keystone Configuration
|
||||
|
||||
For secure operation of the Monasca API, the API must be configured to use Keystone in the configuration file under the middleware section. Monasca only works with a Keystone v3 server. The important parts of the configuration are explained below:
|
||||
|
||||
* serverVIP - This is the hostname or IP Address of the Keystone server
|
||||
* serverPort - The port for the Keystone server
|
||||
* useHttps - Whether to use https when making requests of the Keystone API
|
||||
* truststore - If useHttps is true and the Keystone server is not using a certificate signed by a public CA recognized by Java, the CA certificate can be placed in a truststore so the Monasca API will trust it, otherwise it will reject the https connection. This must be a JKS truststore
|
||||
* truststorePassword - The password for the above truststore
|
||||
* connSSLClientAuth - If the Keystone server requires the SSL client used by the Monasca server to have a specific client certificate, this should be true, false otherwise
|
||||
* keystore - The keystore holding the SSL Client certificate if connSSLClientAuth is true
|
||||
* keystorePassword - The password for the keystore
|
||||
* defaultAuthorizedRoles - An array of roles that authorize a user to access the complete Monasca API. User must have at least one of these roles. See below
|
||||
* agentAuthorizedRoles - An array of roles that authorize only the posting of logs. See Keystone Roles below
|
||||
* adminAuthMethod - "password" if the Monasca API should adminUser and adminPassword to login to the Keystone server to check the user's token, "token" if the Monasca API should use adminToken
|
||||
* adminUser - Admin user name
|
||||
* adminPassword - Admin user password
|
||||
* adminProjectId - Specify the project ID the api should use to request an admin token. Defaults to the admin user's default project. The adminProjectId option takes precedence over adminProjectName.
|
||||
* adminProjectName - Specify the project name the api should use to request an admin token. Defaults to the admin user's default project. The adminProjectId option takes precedence over adminProjectName.
|
||||
* adminToken - A valid admin user token if adminAuthMethod is token
|
||||
* timeToCacheToken - How long the Monasca API should cache the user's token before checking it again
|
||||
|
||||
### Keystone Roles
|
||||
|
||||
See [Monasca API documentation](https://github.com/stackforge/monasca-api/blob/master/README.md#keystone-roles) for the levels of access description.
|
||||
|
||||
## Design Overview
|
||||
|
||||
### Architectural layers
|
||||
|
||||
Requests flow through the following architectural layers from top to bottom:
|
||||
|
||||
* Resource
|
||||
* Serves as the entrypoint into the service.
|
||||
* Responsible for handling web service requests, and performing structural request validation.
|
||||
* Application
|
||||
* Responsible for providing application level implementations for specific use cases.
|
||||
* Domain
|
||||
* Contains the technology agnostic core domain model and domain service definitions.
|
||||
* Responsible for upholding invariants and defining state transitions.
|
||||
* Infrastructure
|
||||
* Contains technology specific implementations of domain services.
|
||||
|
||||
## Documentation
|
||||
|
||||
* API Specification: [/docs/monasca-log-api-spec.md](/docs/monasca-log-api-spec.md).
|
|
@ -0,0 +1,29 @@
|
|||
#!/bin/sh
|
||||
set -x
|
||||
ME=`whoami`
|
||||
echo "Running as user: $ME"
|
||||
MVN=$1
|
||||
VERSION=$2
|
||||
|
||||
check_user() {
|
||||
ME=$1
|
||||
if [ "${ME}" != "jenkins" ]; then
|
||||
echo "\nERROR: Download monasca-common and do a mvn install to install the monasca-commom jars\n" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
BUILD_COMMON=false
|
||||
POM_FILE=~/.m2/repository/monasca-common/monasca-common/${VERSION}/monasca-common-${VERSION}.pom
|
||||
if [ ! -r "${POM_FILE}" ]; then
|
||||
check_user ${ME}
|
||||
BUILD_COMMON=true
|
||||
fi
|
||||
|
||||
# This should only be done on the stack forge system
|
||||
if [ "${BUILD_COMMON}" = "true" ]; then
|
||||
git clone https://github.com/stackforge/monasca-common
|
||||
cd monasca-common
|
||||
${MVN} clean
|
||||
${MVN} install
|
||||
fi
|
|
@ -0,0 +1,86 @@
|
|||
# Monasca Log API
|
||||
|
||||
Date: August 19, 2015
|
||||
|
||||
Document Version: v2.0
|
||||
|
||||
# Log
|
||||
The log resource allows logs to be created.
|
||||
|
||||
## Create Log
|
||||
Create log.
|
||||
|
||||
### POST /v2.0/log/single
|
||||
|
||||
#### Headers
|
||||
* X-Auth-Token (string, required) - Keystone auth token
|
||||
* Content-Type (string, required) - application/json; text/plain
|
||||
* X-Application-Type (string(255), optional) - Type of application
|
||||
* X-Dimensions ({string(255):string(255)}, optional) - A dictionary consisting of (key, value) pairs used to structure logs.
|
||||
|
||||
#### Path Parameters
|
||||
None.
|
||||
|
||||
#### Query Parameters
|
||||
* tenant_id (string, optional, restricted) - Tenant ID to create log on behalf of. Usage of this query parameter requires the `monitoring-delegate` role.
|
||||
|
||||
#### Request Body
|
||||
Consists of a single plain text message or a JSON object which can have a maximum length of 1048576 characters.
|
||||
|
||||
#### Request Examples
|
||||
|
||||
##### Plain text log - single line
|
||||
POST a single line of plain text log.
|
||||
|
||||
```
|
||||
POST /v2.0/log/single HTTP/1.1
|
||||
Host: 192.168.10.4:8080
|
||||
Content-Type: text/plain
|
||||
X-Auth-Token: 27feed73a0ce4138934e30d619b415b0
|
||||
X-Application-Type: apache
|
||||
X-Dimensions: applicationname:WebServer01,environment:production
|
||||
Cache-Control: no-cache
|
||||
|
||||
Hello World
|
||||
```
|
||||
|
||||
##### Plain text log - multi lines
|
||||
POST a multiple lines of plain text log.
|
||||
|
||||
```
|
||||
POST /v2.0/log/single HTTP/1.1
|
||||
Host: 192.168.10.4:8080
|
||||
Content-Type: text/plain
|
||||
X-Auth-Token: 27feed73a0ce4138934e30d619b415b0
|
||||
X-Application-Type: apache
|
||||
X-Dimensions: applicationname:WebServer01,environment:production
|
||||
Cache-Control: no-cache
|
||||
|
||||
Hello\nWorld
|
||||
```
|
||||
|
||||
##### JSON log
|
||||
POST a JSON log
|
||||
|
||||
```
|
||||
POST /v2.0/log/single HTTP/1.1
|
||||
Host: 192.168.10.4:8080
|
||||
Content-Type: application/json
|
||||
X-Auth-Token: 27feed73a0ce4138934e30d619b415b0
|
||||
X-Application-Type: apache
|
||||
X-Dimensions: applicationname:WebServer01,environment:production
|
||||
Cache-Control: no-cache
|
||||
|
||||
{
|
||||
"message":"Hello World!",
|
||||
"from":"hoover"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Response
|
||||
#### Status Code
|
||||
* 204 - No content
|
||||
|
||||
#### Response Body
|
||||
This request does not return a response body.
|
|
@ -0,0 +1,439 @@
|
|||
<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>monasca-log-api</groupId>
|
||||
<artifactId>monasca-log-api</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<url>http://github.com/stackforge/monasca-log-api</url>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<prerequisites>
|
||||
<maven>3.0</maven>
|
||||
</prerequisites>
|
||||
|
||||
<properties>
|
||||
<gitRevision></gitRevision>
|
||||
<timestamp>${maven.build.timestamp}</timestamp>
|
||||
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss</maven.build.timestamp.format>
|
||||
<computedVersion>${project.version}-${timestamp}-${gitRevision}</computedVersion>
|
||||
<computedName>${project.artifactId}-${computedVersion}</computedName>
|
||||
<mon.common.version>1.1.0-SNAPSHOT</mon.common.version>
|
||||
<dropwizard.version>0.7.0</dropwizard.version>
|
||||
|
||||
<skipITs>false</skipITs>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<shadedJarName>${project.artifactId}-${project.version}-shaded
|
||||
</shadedJarName>
|
||||
</properties>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git@github.com:stackforge/monasca-log-api</connection>
|
||||
<developerConnection>scm:git:git@github.com:stackforge/monasca-log-api</developerConnection>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release-deploy-url-override</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>BUILD_NUM</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<computedVersion>${versionNumber}.${BUILD_NUM}</computedVersion>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>monasca-common</groupId>
|
||||
<artifactId>monasca-common-model</artifactId>
|
||||
<version>${mon.common.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-validator</groupId>
|
||||
<artifactId>commons-validator</artifactId>
|
||||
<version>1.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>monasca-common</groupId>
|
||||
<artifactId>monasca-common-kafka</artifactId>
|
||||
<version>${mon.common.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>monasca-common</groupId>
|
||||
<artifactId>monasca-common-middleware</artifactId>
|
||||
<version>${mon.common.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-core</artifactId>
|
||||
<version>${dropwizard.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-assets</artifactId>
|
||||
<version>${dropwizard.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-jersey</artifactId>
|
||||
<version>${dropwizard.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-recipes</artifactId>
|
||||
<version>2.2.0-incubating</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.kafka</groupId>
|
||||
<artifactId>kafka_2.9.2</artifactId>
|
||||
<version>0.8.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jdmk</groupId>
|
||||
<artifactId>jmxtools</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jmx</groupId>
|
||||
<artifactId>jmxri</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>monasca-common</groupId>
|
||||
<artifactId>monasca-common-testing</artifactId>
|
||||
<version>${mon.common.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>monasca-common</groupId>
|
||||
<artifactId>monasca-common-dropwizard</artifactId>
|
||||
<version>${mon.common.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard</groupId>
|
||||
<artifactId>dropwizard-testing</artifactId>
|
||||
<version>${dropwizard.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.docker-java</groupId>
|
||||
<artifactId>docker-java</artifactId>
|
||||
<version>0.9.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jayway.restassured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<version>2.3.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<version>6.8.8</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.10.19</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>2.5</version>
|
||||
<configuration>
|
||||
<filesets>
|
||||
<fileset>
|
||||
<directory>${project.basedir}/debs</directory>
|
||||
</fileset>
|
||||
</filesets>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
<version>1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>create</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<doCheck>false</doCheck>
|
||||
<shortRevisionLength>6</shortRevisionLength>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.17</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.surefire</groupId>
|
||||
<artifactId>surefire-testng</artifactId>
|
||||
<version>2.17</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<excludedGroups>
|
||||
</excludedGroups>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>2.17</version>
|
||||
<configuration>
|
||||
<groups></groups>
|
||||
<skipTests>${skipITs}</skipTests>
|
||||
<parallel>methods</parallel>
|
||||
<threadCount>4</threadCount>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>1.2</version>
|
||||
<configuration>
|
||||
<finalName>${computedName}</finalName>
|
||||
<createDependencyReducedPom>true</createDependencyReducedPom>
|
||||
<filters>
|
||||
<filter>
|
||||
<!-- *:* can't be used for artifact because we are using an older shade plugin -->
|
||||
<artifact>org.eclipse.jetty.orbit:javax.servlet</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>org.hamcrest:hamcrest-core</exclude>
|
||||
<exclude>org.hamcrest:hamcrest-library</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>monasca.api.MonApiApplication</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<packageName>monasca.api</packageName>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Implementation-Version>${project.artifactId}-${computedVersion}</Implementation-Version>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.4.1</version>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/assembly/tar.xml</descriptor>
|
||||
</descriptors>
|
||||
<finalName>${artifactNamedVersion}</finalName>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>jdeb</artifactId>
|
||||
<groupId>org.vafer</groupId>
|
||||
<version>1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jdeb</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<deb>${project.basedir}/debs/binaries/${computedName}.deb</deb>
|
||||
<dataSet>
|
||||
<data>
|
||||
<type>file</type>
|
||||
<src>${project.build.directory}/${shadedJarName}.jar</src>
|
||||
<dst>/opt/monasca/monasca-api.jar</dst>
|
||||
</data>
|
||||
<data>
|
||||
<type>file</type>
|
||||
<src>${project.basedir}/src/deb/etc/log-api-config.yml-sample
|
||||
</src>
|
||||
<dst>/etc/monasca/log-api-config.yml-sample</dst>
|
||||
</data>
|
||||
</dataSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-scm-plugin</artifactId>
|
||||
<version>1.9.2</version>
|
||||
<configuration>
|
||||
<tag>${project.version}</tag>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.7.5.201505241946</version>
|
||||
<configuration>
|
||||
<destFile>${basedir}/target/coverage-reports/jacoco-unit.exec</destFile>
|
||||
<dataFile>${basedir}/target/coverage-reports/jacoco-unit.exec</dataFile>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>jacoco-initialize</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>jacoco-site</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.7.5.201505241946</version>
|
||||
<configuration>
|
||||
<destFile>${basedir}/target/coverage-reports/jacoco-unit.exec</destFile>
|
||||
<dataFile>${basedir}/target/coverage-reports/jacoco-unit.exec</dataFile>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>jacoco-initialize</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>jacoco-site</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,29 @@
|
|||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
|
||||
<id>tar</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>README*</include>
|
||||
<include>LICENSE*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<files>
|
||||
<file>
|
||||
<source>${project.build.directory}/${shadedJarName}.jar</source>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<destName>monasca-log-api.jar</destName>
|
||||
</file>
|
||||
<file>
|
||||
<source>${project.basedir}/src/deb/etc/log-api-config.yml-sample</source>
|
||||
<outputDirectory>examples</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
</assembly>
|
|
@ -0,0 +1,9 @@
|
|||
Package: [[name]]
|
||||
Section: misc
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Depends: openjdk-7-jre-headless | openjdk-7-jre
|
||||
Version: [[version]]-[[timestamp]]-[[buildNumber]]
|
||||
Maintainer: Monasca Team <monasca@lists.launchpad.net>
|
||||
Description: Monasca-LOG-API
|
||||
RESTful API for Monasca logs.
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh
|
||||
|
||||
case "$1" in
|
||||
remove)
|
||||
stop monasca-log-api
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,72 @@
|
|||
# The region for which all metrics passing through this server will be persisted
|
||||
region: useast
|
||||
|
||||
kafka:
|
||||
brokerUris:
|
||||
- 192.168.10.4:9092
|
||||
zookeeperUris:
|
||||
- 192.168.10.4:2181
|
||||
healthCheckTopic: healthcheck
|
||||
|
||||
middleware:
|
||||
enabled: true
|
||||
serverVIP: 192.168.10.5
|
||||
serverPort: 5000
|
||||
connTimeout: 500
|
||||
connSSLClientAuth: false
|
||||
connPoolMaxActive: 3
|
||||
connPoolMaxIdle: 3
|
||||
connPoolEvictPeriod: 600000
|
||||
connPoolMinIdleTime: 600000
|
||||
connRetryTimes: 2
|
||||
connRetryInterval: 50
|
||||
defaultAuthorizedRoles: [user, domainuser, domainadmin, monasca-user]
|
||||
agentAuthorizedRoles: [monasca-agent]
|
||||
adminAuthMethod: password
|
||||
adminUser: admin
|
||||
adminPassword: admin
|
||||
adminProjectId:
|
||||
adminProjectName:
|
||||
adminToken:
|
||||
timeToCacheToken: 600
|
||||
maxTokenCacheSize: 1048576
|
||||
|
||||
server:
|
||||
applicationConnectors:
|
||||
- type: http
|
||||
maxRequestHeaderSize: 16KiB # Allow large headers used by keystone tokens
|
||||
|
||||
# Logging settings.
|
||||
logging:
|
||||
|
||||
# The default level of all loggers. Can be OFF, ERROR, WARN, INFO, DEBUG, TRACE, or ALL.
|
||||
level: debug
|
||||
|
||||
# Logger-specific levels.
|
||||
loggers:
|
||||
|
||||
# Sets the level for 'com.example.app' to DEBUG.
|
||||
com.example.app: DEBUG
|
||||
|
||||
appenders:
|
||||
- type: console
|
||||
threshold: ALL
|
||||
timeZone: UTC
|
||||
target: stdout
|
||||
logFormat: # TODO
|
||||
|
||||
- type: file
|
||||
currentLogFilename: /var/log/monasca/monasca-api.log
|
||||
threshold: ALL
|
||||
archive: true
|
||||
archivedLogFilenamePattern: /var/log/monasca/monasca-api-%d.log.gz
|
||||
archivedFileCount: 5
|
||||
timeZone: UTC
|
||||
logFormat: # TODO
|
||||
|
||||
- type: syslog
|
||||
host: 192.168.10.4
|
||||
port: 514
|
||||
facility: local0
|
||||
threshold: ALL
|
||||
logFormat: # TODO
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api;
|
||||
|
||||
import io.dropwizard.Configuration;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import monasca.common.messaging.kafka.KafkaConfiguration;
|
||||
import monasca.log.api.infrastructure.middleware.MiddlewareConfiguration;
|
||||
|
||||
public class ApiConfig extends Configuration {
|
||||
@NotEmpty
|
||||
public String region;
|
||||
@NotEmpty
|
||||
public String logTopic = "log";
|
||||
@Valid
|
||||
@NotNull
|
||||
public KafkaConfiguration kafka;
|
||||
@Valid
|
||||
@NotNull
|
||||
public MiddlewareConfiguration middleware;
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api;
|
||||
|
||||
import io.dropwizard.Application;
|
||||
import io.dropwizard.setup.Bootstrap;
|
||||
import io.dropwizard.setup.Environment;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.FilterRegistration.Dynamic;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
|
||||
import org.eclipse.jetty.servlets.CrossOriginFilter;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
|
||||
import monasca.common.middleware.AuthConstants;
|
||||
import monasca.common.middleware.TokenAuth;
|
||||
import monasca.common.util.Injector;
|
||||
import monasca.log.api.infrastructure.servlet.MockAuthenticationFilter;
|
||||
import monasca.log.api.infrastructure.servlet.PostAuthenticationFilter;
|
||||
import monasca.log.api.infrastructure.servlet.PreAuthenticationFilter;
|
||||
import monasca.log.api.infrastructure.servlet.RoleAuthorizationFilter;
|
||||
import monasca.log.api.resource.LogResource;
|
||||
import monasca.log.api.resource.exception.ConstraintViolationExceptionMapper;
|
||||
import monasca.log.api.resource.exception.IllegalArgumentExceptionMapper;
|
||||
import monasca.log.api.resource.exception.JsonMappingExceptionManager;
|
||||
import monasca.log.api.resource.exception.JsonProcessingExceptionMapper;
|
||||
import monasca.log.api.resource.exception.ThrowableExceptionMapper;
|
||||
|
||||
/**
|
||||
* Monitoring API application.
|
||||
*/
|
||||
public class MonApiApplication extends Application<ApiConfig> {
|
||||
public static void main(String[] args) throws Exception {
|
||||
/*
|
||||
* This should allow command line options to show the current version
|
||||
* java -jar monasca-log-api.jar --version
|
||||
* java -jar monasca-log-api.jar -version
|
||||
* java -jar monasca-log-api.jar version
|
||||
* Really anything with the word version in it will show the
|
||||
* version as long as there is only one argument
|
||||
* */
|
||||
if (args.length == 1 && args[0].toLowerCase().contains("version")) {
|
||||
showVersion();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
new MonApiApplication().run(args);
|
||||
}
|
||||
|
||||
private static void showVersion() {
|
||||
Package pkg;
|
||||
pkg = Package.getPackage("monasca.log.api");
|
||||
|
||||
System.out.println("-------- Version Information --------");
|
||||
System.out.println(pkg.getImplementationVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(Bootstrap<ApiConfig> bootstrap) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Fujitsu LOG service";
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run(ApiConfig config, Environment environment) throws Exception {
|
||||
/** Wire services */
|
||||
Injector.registerModules(new MonApiModule(config));
|
||||
|
||||
/** Configure resources */
|
||||
environment.jersey().register(Injector.getInstance(LogResource.class));
|
||||
|
||||
/** Configure providers */
|
||||
removeExceptionMappers(environment.jersey().getResourceConfig().getSingletons());
|
||||
environment.jersey().register(new IllegalArgumentExceptionMapper());
|
||||
environment.jersey().register(new JsonProcessingExceptionMapper());
|
||||
environment.jersey().register(new JsonMappingExceptionManager());
|
||||
environment.jersey().register(new ConstraintViolationExceptionMapper());
|
||||
environment.jersey().register(new ThrowableExceptionMapper<Throwable>() {});
|
||||
|
||||
/** Configure Jackson */
|
||||
environment.getObjectMapper().setPropertyNamingStrategy(
|
||||
PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
|
||||
environment.getObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
|
||||
environment.getObjectMapper().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
SimpleModule module = new SimpleModule("SerializationModule");
|
||||
environment.getObjectMapper().registerModule(module);
|
||||
|
||||
|
||||
/** Configure CORS filter */
|
||||
Dynamic corsFilter = environment.servlets().addFilter("cors", CrossOriginFilter.class);
|
||||
corsFilter.addMappingForUrlPatterns(null, true, "/*");
|
||||
corsFilter.setInitParameter("allowedOrigins", "*");
|
||||
corsFilter.setInitParameter("allowedHeaders",
|
||||
"X-Requested-With,Content-Type,Accept,Origin,X-Auth-Token");
|
||||
corsFilter.setInitParameter("allowedMethods", "OPTIONS,GET,HEAD");
|
||||
|
||||
if (config.middleware.enabled) {
|
||||
ensureHasValue(config.middleware.serverVIP, "serverVIP", "enabled", "true");
|
||||
ensureHasValue(config.middleware.serverPort, "serverPort", "enabled", "true");
|
||||
ensureHasValue(config.middleware.adminAuthMethod, "adminAuthMethod", "enabled", "true");
|
||||
if ("password".equalsIgnoreCase(config.middleware.adminAuthMethod)) {
|
||||
ensureHasValue(config.middleware.adminUser, "adminUser", "adminAuthMethod", "password");
|
||||
ensureHasValue(config.middleware.adminPassword, "adminPassword", "adminAuthMethod", "password");
|
||||
} else if ("token".equalsIgnoreCase(config.middleware.adminAuthMethod)) {
|
||||
ensureHasValue(config.middleware.adminToken, "adminToken", "adminAuthMethod", "token");
|
||||
} else {
|
||||
throw new Exception(String.format(
|
||||
"Invalid value '%s' for adminAuthMethod. Must be either password or token",
|
||||
config.middleware.adminAuthMethod));
|
||||
}
|
||||
if (config.middleware.defaultAuthorizedRoles == null || config.middleware.defaultAuthorizedRoles.isEmpty()) {
|
||||
ensureHasValue(null, "defaultAuthorizedRoles", "enabled", "true");
|
||||
}
|
||||
if (config.middleware.connSSLClientAuth) {
|
||||
ensureHasValue(config.middleware.keystore, "keystore", "connSSLClientAuth", "true");
|
||||
ensureHasValue(config.middleware.keystorePassword, "keystorePassword", "connSSLClientAuth", "true");
|
||||
}
|
||||
Map<String, String> authInitParams = new HashMap<String, String>();
|
||||
authInitParams.put("ServerVIP", config.middleware.serverVIP);
|
||||
authInitParams.put("ServerPort", config.middleware.serverPort);
|
||||
authInitParams.put(AuthConstants.USE_HTTPS, String.valueOf(config.middleware.useHttps));
|
||||
authInitParams.put("ConnTimeout", config.middleware.connTimeout);
|
||||
authInitParams.put("ConnSSLClientAuth", String.valueOf(config.middleware.connSSLClientAuth));
|
||||
authInitParams.put("ConnPoolMaxActive", config.middleware.connPoolMaxActive);
|
||||
authInitParams.put("ConnPoolMaxIdle", config.middleware.connPoolMaxActive);
|
||||
authInitParams.put("ConnPoolEvictPeriod", config.middleware.connPoolEvictPeriod);
|
||||
authInitParams.put("ConnPoolMinIdleTime", config.middleware.connPoolMinIdleTime);
|
||||
authInitParams.put("ConnRetryTimes", config.middleware.connRetryTimes);
|
||||
authInitParams.put("ConnRetryInterval", config.middleware.connRetryInterval);
|
||||
authInitParams.put("AdminToken", config.middleware.adminToken);
|
||||
authInitParams.put("TimeToCacheToken", config.middleware.timeToCacheToken);
|
||||
authInitParams.put("AdminAuthMethod", config.middleware.adminAuthMethod);
|
||||
authInitParams.put("AdminUser", config.middleware.adminUser);
|
||||
authInitParams.put("AdminPassword", config.middleware.adminPassword);
|
||||
authInitParams.put(AuthConstants.ADMIN_PROJECT_ID, config.middleware.adminProjectId);
|
||||
authInitParams.put(AuthConstants.ADMIN_PROJECT_NAME, config.middleware.adminProjectName);
|
||||
authInitParams.put("MaxTokenCacheSize", config.middleware.maxTokenCacheSize);
|
||||
setIfNotNull(authInitParams, AuthConstants.TRUSTSTORE, config.middleware.truststore);
|
||||
setIfNotNull(authInitParams, AuthConstants.TRUSTSTORE_PASS, config.middleware.truststorePassword);
|
||||
setIfNotNull(authInitParams, AuthConstants.KEYSTORE, config.middleware.keystore);
|
||||
setIfNotNull(authInitParams, AuthConstants.KEYSTORE_PASS, config.middleware.keystorePassword);
|
||||
|
||||
/** Configure auth filters */
|
||||
Dynamic preAuthenticationFilter =
|
||||
environment.servlets().addFilter("pre-auth", new PreAuthenticationFilter());
|
||||
preAuthenticationFilter.addMappingForUrlPatterns(null, true, "/");
|
||||
preAuthenticationFilter.addMappingForUrlPatterns(null, true, "/v2.0/*");
|
||||
|
||||
Dynamic tokenAuthFilter = environment.servlets().addFilter("token-auth", new TokenAuth());
|
||||
tokenAuthFilter.addMappingForUrlPatterns(null, true, "/");
|
||||
tokenAuthFilter.addMappingForUrlPatterns(null, true, "/v2.0/*");
|
||||
tokenAuthFilter.setInitParameters(authInitParams);
|
||||
|
||||
Dynamic postAuthenticationFilter =
|
||||
environment.servlets().addFilter(
|
||||
"post-auth",
|
||||
new PostAuthenticationFilter(config.middleware.defaultAuthorizedRoles,
|
||||
config.middleware.agentAuthorizedRoles));
|
||||
postAuthenticationFilter.addMappingForUrlPatterns(null, true, "/");
|
||||
postAuthenticationFilter.addMappingForUrlPatterns(null, true, "/v2.0/*");
|
||||
|
||||
environment.jersey().getResourceConfig().getContainerRequestFilters()
|
||||
.add(new RoleAuthorizationFilter());
|
||||
} else {
|
||||
Dynamic mockAuthenticationFilter =
|
||||
environment.servlets().addFilter("mock-auth", new MockAuthenticationFilter());
|
||||
mockAuthenticationFilter.addMappingForUrlPatterns(null, true, "/");
|
||||
mockAuthenticationFilter.addMappingForUrlPatterns(null, true, "/v2.0/*");
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureHasValue(final String value, final String what, final String control,
|
||||
final String controlValue) throws Exception {
|
||||
if (value == null || value.isEmpty()) {
|
||||
final String message =
|
||||
String
|
||||
.format(
|
||||
"Since %s in middleware section of configuration file is set to %s, %s must have a value",
|
||||
control, controlValue, what);
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void setIfNotNull(Map<String, String> authInitParams, String name, String value) {
|
||||
if (value != null) {
|
||||
authInitParams.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeExceptionMappers(Set<Object> items) {
|
||||
for (Iterator<Object> i = items.iterator(); i.hasNext();) {
|
||||
Object o = i.next();
|
||||
if (o instanceof ExceptionMapper)
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import kafka.javaapi.producer.Producer;
|
||||
import kafka.producer.ProducerConfig;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
import monasca.log.api.app.ApplicationModule;
|
||||
|
||||
/**
|
||||
* Monitoring API server bindings.
|
||||
*/
|
||||
public class MonApiModule extends AbstractModule {
|
||||
private final ApiConfig config;
|
||||
|
||||
public MonApiModule(ApiConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ApiConfig.class).toInstance(config);
|
||||
install(new ApplicationModule());
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public Producer<String, String> getProducer() {
|
||||
Properties props = new Properties();
|
||||
props.put("metadata.broker.list", Joiner.on(',').join(config.kafka.brokerUris));
|
||||
props.put("serializer.class", "kafka.serializer.StringEncoder");
|
||||
props.put("request.required.acks", "1");
|
||||
ProducerConfig config = new ProducerConfig(props);
|
||||
return new Producer<String, String>(config);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.app;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
/**
|
||||
* Application layer bindings.
|
||||
*/
|
||||
public class ApplicationModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(LogService.class).in(Singleton.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2015 Fujitsu Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.app;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import kafka.javaapi.producer.Producer;
|
||||
import kafka.producer.KeyedMessage;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMap.Builder;
|
||||
|
||||
import monasca.log.api.ApiConfig;
|
||||
import monasca.log.api.model.Log;
|
||||
import monasca.log.api.model.LogEnvelope;
|
||||
import monasca.log.api.model.LogEnvelopes;
|
||||
|
||||
/**
|
||||
* Log service implementation.
|
||||
*/
|
||||
public class LogService {
|
||||
private static final Comparator<Map.Entry<String, String>> comparator;
|
||||
private final ApiConfig config;
|
||||
private final Producer<String, String> producer;
|
||||
|
||||
static {
|
||||
comparator = new Comparator<Map.Entry<String, String>>() {
|
||||
@Override
|
||||
public int compare(Entry<String, String> o1, Entry<String, String> o2) {
|
||||
int nameCmp = o1.getKey().compareTo(o2.getKey());
|
||||
return (nameCmp != 0 ? nameCmp : o1.getValue().compareTo(o2.getValue()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Inject
|
||||
public LogService(ApiConfig config, Producer<String, String> producer) {
|
||||
this.config = config;
|
||||
this.producer = producer;
|
||||
}
|
||||
|
||||
public void create(Log log, String tenantId) {
|
||||
Builder<String, Object> metaBuilder = new ImmutableMap.Builder<String, Object>().put("tenantId", tenantId).put("region", this.config.region);
|
||||
ImmutableMap<String, Object> meta = metaBuilder.build();
|
||||
|
||||
LogEnvelope envelope = new LogEnvelope(log, meta);
|
||||
String key = buildKey(tenantId, log);
|
||||
String json = LogEnvelopes.toJson(envelope);
|
||||
String topic = this.config.logTopic;
|
||||
this.producer.send(new KeyedMessage<>(topic, key, json));
|
||||
}
|
||||
|
||||
private String buildKey(String tenantId, Log log) {
|
||||
final StringBuilder key = new StringBuilder();
|
||||
|
||||
// appends tenantId
|
||||
key.append(tenantId);
|
||||
|
||||
// appends applicationType
|
||||
if (log.applicationType != null && !log.applicationType.isEmpty()) {
|
||||
key.append(log.applicationType);
|
||||
}
|
||||
|
||||
// appends dimensions
|
||||
if (log.dimensions != null && !log.dimensions.isEmpty()) {
|
||||
for (final Map.Entry<String, String> dim : buildSortedDimSet(log.dimensions)) {
|
||||
key.append(dim.getKey());
|
||||
key.append(dim.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
// Key must be the same for the same log so sort the dimensions so they will
|
||||
// be
|
||||
// in a known order
|
||||
private List<Map.Entry<String, String>> buildSortedDimSet(final Map<String, String> dimMap) {
|
||||
final List<Map.Entry<String, String>> dims = new ArrayList<>(dimMap.entrySet());
|
||||
Collections.sort(dims, comparator);
|
||||
return dims;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright 2015 Fujitsu Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.app.command;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import monasca.log.api.app.validation.DimensionValidation;
|
||||
import monasca.log.api.app.validation.LogApplicationTypeValidator;
|
||||
import monasca.log.api.model.Log;
|
||||
import monasca.log.api.resource.exception.Exceptions;
|
||||
|
||||
public class CreateLogCommand {
|
||||
public static final int MAX_NAME_LENGTH = 255;
|
||||
public static final int MAX_LOG_LENGTH = 1024 * 1024;
|
||||
|
||||
@NotEmpty
|
||||
@Size(min = 1, max = MAX_NAME_LENGTH)
|
||||
public String applicationType;
|
||||
public Map<String, String> dimensions;
|
||||
public String message;
|
||||
|
||||
public CreateLogCommand() {}
|
||||
|
||||
public CreateLogCommand(@Nullable String applicationType, @Nullable Map<String, String> dimensions, String message) {
|
||||
setApplicationType(applicationType);
|
||||
setDimensions(dimensions);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void setDimensions(Map<String, String> dimensions) {
|
||||
// White spaces have been already trimmed, but normalize just in case.
|
||||
this.dimensions = DimensionValidation.normalize(dimensions);
|
||||
}
|
||||
|
||||
public void setApplicationType(String applicationType) {
|
||||
this.applicationType = LogApplicationTypeValidator.normalize(applicationType);
|
||||
}
|
||||
|
||||
public Log toLog() {
|
||||
return new Log(applicationType, dimensions, message);
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
// Validate applicationType
|
||||
if (applicationType != null && !applicationType.isEmpty()) {
|
||||
LogApplicationTypeValidator.validate(applicationType);
|
||||
}
|
||||
|
||||
// Validate dimensions
|
||||
if (dimensions != null) {
|
||||
DimensionValidation.validate(dimensions, null);
|
||||
}
|
||||
|
||||
// Validate log message
|
||||
if (message.length() > MAX_LOG_LENGTH)
|
||||
throw Exceptions.unprocessableEntity("Log must be %d characters or less", MAX_LOG_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
CreateLogCommand other = (CreateLogCommand) obj;
|
||||
if (dimensions == null) {
|
||||
if (other.dimensions != null)
|
||||
return false;
|
||||
} else if (!dimensions.equals(other.dimensions))
|
||||
return false;
|
||||
if (applicationType == null) {
|
||||
if (other.applicationType != null)
|
||||
return false;
|
||||
} else if (!applicationType.equals(other.applicationType))
|
||||
return false;
|
||||
if (!message.equals(other.message))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((dimensions == null) ? 0 : dimensions.hashCode());
|
||||
result = prime * result + ((applicationType == null) ? 0 : applicationType.hashCode());
|
||||
result = prime * result * message.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Houses the application/service layer.
|
||||
*
|
||||
* @see http://martinfowler.com/eaaCatalog/serviceLayer.html
|
||||
*/
|
||||
package monasca.log.api.app;
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.app.validation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
import monasca.common.model.Services;
|
||||
import monasca.log.api.resource.exception.Exceptions;
|
||||
|
||||
/**
|
||||
* Utilities for validating dimensions.
|
||||
*/
|
||||
public final class DimensionValidation {
|
||||
private static final Map<String, DimensionValidator> VALIDATORS;
|
||||
private static final Pattern UUID_PATTERN = Pattern
|
||||
.compile("\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}");
|
||||
private static final Pattern VALID_DIMENSION_NAME = Pattern.compile("[^><={}(), '\";&]+$");
|
||||
private static final String INVALID_CHAR_STRING = "> < = { } ( ) ' \" , ; &";
|
||||
|
||||
private DimensionValidation() {}
|
||||
|
||||
interface DimensionValidator {
|
||||
boolean isValidDimension(String name, String value);
|
||||
}
|
||||
|
||||
static {
|
||||
VALIDATORS = new HashMap<String, DimensionValidator>();
|
||||
|
||||
// Compute validator
|
||||
VALIDATORS.put(Services.COMPUTE_SERVICE, new DimensionValidator() {
|
||||
@Override
|
||||
public boolean isValidDimension(String name, String value) {
|
||||
if ("instance_id".equals(name))
|
||||
return value.length() != 36 || UUID_PATTERN.matcher(value).matches();
|
||||
if ("az".equals(name))
|
||||
return Ints.tryParse(value) != null;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Objectstore validator
|
||||
VALIDATORS.put(Services.OBJECT_STORE_SERVICE, new DimensionValidator() {
|
||||
@Override
|
||||
public boolean isValidDimension(String name, String value) {
|
||||
if ("container".equals(name))
|
||||
return value.length() < 256 || !value.contains("/");
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Volume validator
|
||||
VALIDATORS.put(Services.VOLUME_SERVICE, new DimensionValidator() {
|
||||
@Override
|
||||
public boolean isValidDimension(String name, String value) {
|
||||
if ("instance_id".equals(name))
|
||||
return value.length() != 36 || UUID_PATTERN.matcher(value).matches();
|
||||
if ("az".equals(name))
|
||||
return Ints.tryParse(value) != null;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes dimensions by stripping whitespace.
|
||||
*/
|
||||
public static Map<String, String> normalize(Map<String, String> dimensions) {
|
||||
if (dimensions == null)
|
||||
return null;
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (Map.Entry<String, String> dimension : dimensions.entrySet()) {
|
||||
String dimensionKey = null;
|
||||
if (dimension.getKey() != null) {
|
||||
dimensionKey = CharMatcher.WHITESPACE.trimFrom(dimension.getKey());
|
||||
if (dimensionKey.isEmpty())
|
||||
dimensionKey = null;
|
||||
}
|
||||
String dimensionValue = null;
|
||||
if (dimension.getValue() != null) {
|
||||
dimensionValue = CharMatcher.WHITESPACE.trimFrom(dimension.getValue());
|
||||
if (dimensionValue.isEmpty())
|
||||
dimensionValue = null;
|
||||
}
|
||||
result.put(dimensionKey, dimensionValue);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given {@code dimensions} are valid.
|
||||
*
|
||||
* @throws WebApplicationException if validation fails
|
||||
*/
|
||||
public static void validate(Map<String, String> dimensions, @Nullable String service) {
|
||||
// Validate dimension names and values
|
||||
for (Map.Entry<String, String> dimension : dimensions.entrySet()) {
|
||||
String name = dimension.getKey();
|
||||
String value = dimension.getValue();
|
||||
|
||||
// General validations
|
||||
if (Strings.isNullOrEmpty(name))
|
||||
throw Exceptions.unprocessableEntity("Dimension name cannot be empty");
|
||||
if (Strings.isNullOrEmpty(value))
|
||||
throw Exceptions.unprocessableEntity("Dimension %s cannot have an empty value", name);
|
||||
if (name.length() > 255)
|
||||
throw Exceptions.unprocessableEntity("Dimension name %s must be 255 characters or less",
|
||||
name);
|
||||
if (value.length() > 255)
|
||||
throw Exceptions.unprocessableEntity("Dimension value %s must be 255 characters or less",
|
||||
value);
|
||||
// Dimension names that start with underscores are reserved for internal use only.
|
||||
if (name.startsWith("_")) {
|
||||
throw Exceptions.unprocessableEntity("Dimension name cannot start with underscore (_)",
|
||||
name);
|
||||
}
|
||||
if (!VALID_DIMENSION_NAME.matcher(name).matches())
|
||||
throw Exceptions.unprocessableEntity(
|
||||
"Dimension name %s may not contain: %s", name, INVALID_CHAR_STRING);
|
||||
|
||||
// Service specific validations
|
||||
if (service != null) {
|
||||
if (!name.equals(Services.SERVICE_DIMENSION)
|
||||
&& !Services.isValidDimensionName(service, name))
|
||||
throw Exceptions.unprocessableEntity("%s is not a valid dimension name for service %s",
|
||||
name, service);
|
||||
DimensionValidator validator = VALIDATORS.get(service);
|
||||
if (validator != null && !validator.isValidDimension(name, value))
|
||||
throw Exceptions.unprocessableEntity("%s is not a valid dimension value for service %s",
|
||||
value, service);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a list of dimension names
|
||||
* @param names
|
||||
*/
|
||||
|
||||
public static void validateNames(List<String> names) {
|
||||
if(names != null) {
|
||||
for (String name : names) {
|
||||
if (Strings.isNullOrEmpty(name)) {
|
||||
throw Exceptions.unprocessableEntity("Dimension name cannot be empty");
|
||||
}
|
||||
if (name.length() > 255) {
|
||||
throw Exceptions.unprocessableEntity("Dimension name '%s' must be 255 characters or less",
|
||||
name);
|
||||
}
|
||||
// Dimension names that start with underscores are reserved for internal use only.
|
||||
if (name.startsWith("_")) {
|
||||
throw Exceptions.unprocessableEntity("Dimension name '%s' cannot start with underscore (_)",
|
||||
name);
|
||||
}
|
||||
if (!VALID_DIMENSION_NAME.matcher(name).matches())
|
||||
throw Exceptions.unprocessableEntity(
|
||||
"Dimension name '%s' may not contain: %s", name, INVALID_CHAR_STRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2015 Fujitsu Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.app.validation;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
|
||||
import monasca.log.api.app.command.CreateLogCommand;
|
||||
import monasca.log.api.resource.exception.Exceptions;
|
||||
|
||||
/**
|
||||
* Utilities for validating log application types.
|
||||
*/
|
||||
public class LogApplicationTypeValidator {
|
||||
private static final Pattern VALID_APPLICATION_TYPE = Pattern.compile("^[a-zA-Z0-9_\\.\\-]+$");
|
||||
|
||||
private LogApplicationTypeValidator() {}
|
||||
|
||||
/**
|
||||
* Normalizes the {@code applicationType} by removing whitespace.
|
||||
*/
|
||||
public static String normalize(String applicationType) {
|
||||
return applicationType == null ? null : CharMatcher.WHITESPACE.trimFrom(applicationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the {@code applicationType} for the character constraints.
|
||||
*
|
||||
* @throws WebApplicationException if validation fails
|
||||
*/
|
||||
public static void validate(String applicationType) {
|
||||
if (applicationType.length() > CreateLogCommand.MAX_NAME_LENGTH)
|
||||
throw Exceptions.unprocessableEntity("Application type %s must be %d characters or less", applicationType, CreateLogCommand.MAX_NAME_LENGTH);
|
||||
|
||||
if (!VALID_APPLICATION_TYPE.matcher(applicationType).matches())
|
||||
throw Exceptions.unprocessableEntity("Application type %s may only contain: a-z A-Z 0-9 _ - .", applicationType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.app.validation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import monasca.log.api.resource.exception.Exceptions;
|
||||
|
||||
/**
|
||||
* Validation related utilities.
|
||||
*/
|
||||
public final class Validation {
|
||||
|
||||
private static final Splitter COMMA_SPLITTER_FOR_LOG = Splitter.on(',').trimResults();
|
||||
|
||||
private Validation() {}
|
||||
|
||||
/**
|
||||
* @throws WebApplicationException if the {@code value} is null or empty.
|
||||
*/
|
||||
public static Map<String, String> parseLogDimensions(String dimensionsStr) {
|
||||
Validation.validateNotNullOrEmpty(dimensionsStr, "dimensions");
|
||||
|
||||
Map<String, String> dimensions = new HashMap<String, String>();
|
||||
for (String dimensionStr : COMMA_SPLITTER_FOR_LOG.split(dimensionsStr)) {
|
||||
if (dimensionStr.isEmpty())
|
||||
throw Exceptions.unprocessableEntity("Dimension cannot be empty");
|
||||
|
||||
int index = dimensionStr.indexOf(':');
|
||||
if (index == -1)
|
||||
throw Exceptions.unprocessableEntity("%s is not a valid dimension", dimensionStr);
|
||||
String dimensionKey = dimensionStr.substring(0, index);
|
||||
String dimensionValue = dimensionStr.substring(index + 1);
|
||||
|
||||
dimensions.put(dimensionKey, dimensionValue);
|
||||
}
|
||||
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws WebApplicationException if the {@code value} is null or empty.
|
||||
*/
|
||||
public static void validateNotNullOrEmpty(String value, String parameterName) {
|
||||
if (Strings.isNullOrEmpty(value))
|
||||
throw Exceptions.unprocessableEntity("%s is required", parameterName);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.app.validation;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
|
||||
import monasca.log.api.resource.exception.Exceptions;
|
||||
|
||||
/**
|
||||
* Utilities for validating valueMeta.
|
||||
*/
|
||||
public final class ValueMetaValidation {
|
||||
private static final int VALUE_META_MAX_NUMBER = 16;
|
||||
private static final int VALUE_META_VALUE_MAX_LENGTH = 2048;
|
||||
private static final int VALUE_META_NAME_MAX_LENGTH = 255;
|
||||
private static final Map<String, String> EMPTY_VALUE_META = Collections
|
||||
.unmodifiableMap(new HashMap<String, String>());
|
||||
|
||||
private ValueMetaValidation() {}
|
||||
|
||||
/**
|
||||
* Normalizes valueMeta by stripping whitespace from name. validate() must
|
||||
* already have been called on the valueMeta
|
||||
*/
|
||||
public static Map<String, String> normalize(Map<String, String> valueMeta) {
|
||||
if (valueMeta == null || valueMeta.isEmpty()) {
|
||||
return EMPTY_VALUE_META;
|
||||
}
|
||||
final Map<String, String> result = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : valueMeta.entrySet()) {
|
||||
final String key = CharMatcher.WHITESPACE.trimFrom(entry.getKey());
|
||||
result.put(key, entry.getValue());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given {@code valueMetas} are valid.
|
||||
*
|
||||
* @throws WebApplicationException if validation fails
|
||||
*/
|
||||
public static void validate(Map<String, String> valueMetas) {
|
||||
if (valueMetas.size() > VALUE_META_MAX_NUMBER) {
|
||||
throw Exceptions.unprocessableEntity("Maximum number of valueMeta key/value parirs is %d",
|
||||
VALUE_META_MAX_NUMBER);
|
||||
}
|
||||
|
||||
// Validate valueMeta names and values
|
||||
for (Map.Entry<String, String> valueMeta : valueMetas.entrySet()) {
|
||||
// Have to check for null first because later check is for trimmed name
|
||||
if (valueMeta.getKey() == null) {
|
||||
throw Exceptions.unprocessableEntity("valueMeta name cannot be empty");
|
||||
}
|
||||
final String name = CharMatcher.WHITESPACE.trimFrom(valueMeta.getKey());
|
||||
String value = valueMeta.getValue();
|
||||
if (value == null) {
|
||||
// Store nulls as empty strings
|
||||
value = "";
|
||||
}
|
||||
|
||||
// General validations
|
||||
if (Strings.isNullOrEmpty(name)) {
|
||||
throw Exceptions.unprocessableEntity("valueMeta name cannot be empty");
|
||||
}
|
||||
if (name.length() > VALUE_META_NAME_MAX_LENGTH) {
|
||||
throw Exceptions.unprocessableEntity("valueMeta name %s must be %d characters or less",
|
||||
name, VALUE_META_NAME_MAX_LENGTH);
|
||||
}
|
||||
if (value.length() > VALUE_META_VALUE_MAX_LENGTH) {
|
||||
throw Exceptions.unprocessableEntity("valueMeta value %s must be %d characters or less",
|
||||
value, VALUE_META_VALUE_MAX_LENGTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.infrastructure.middleware;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* CS Middleware configuration.
|
||||
*/
|
||||
public class MiddlewareConfiguration {
|
||||
public Boolean enabled = false;
|
||||
@JsonProperty
|
||||
public String serverVIP;
|
||||
@JsonProperty
|
||||
public String serverPort;
|
||||
@JsonProperty
|
||||
public Boolean useHttps = Boolean.FALSE;
|
||||
@JsonProperty
|
||||
public String connTimeout = "500";
|
||||
@JsonProperty
|
||||
public Boolean connSSLClientAuth = Boolean.FALSE;
|
||||
@JsonProperty
|
||||
public String connPoolMaxActive = "3";
|
||||
@JsonProperty
|
||||
public String connPoolMaxIdle = "3";
|
||||
@JsonProperty
|
||||
public String connPoolEvictPeriod = "600000";
|
||||
@JsonProperty
|
||||
public String connPoolMinIdleTime = "600000";
|
||||
@JsonProperty
|
||||
public String connRetryTimes = "2";
|
||||
@JsonProperty
|
||||
public String connRetryInterval = "50";
|
||||
@JsonProperty
|
||||
public List<String> defaultAuthorizedRoles;
|
||||
@JsonProperty
|
||||
public List<String> agentAuthorizedRoles;
|
||||
@JsonProperty
|
||||
public String delegateAuthorizedRole;
|
||||
@JsonProperty
|
||||
public String timeToCacheToken = "600";
|
||||
@JsonProperty
|
||||
public String adminAuthMethod;
|
||||
@JsonProperty
|
||||
public String adminUser;
|
||||
@JsonProperty
|
||||
public String adminToken;
|
||||
@JsonProperty
|
||||
public String adminPassword;
|
||||
@JsonProperty
|
||||
public String adminProjectId = "";
|
||||
@JsonProperty
|
||||
public String adminProjectName = "";
|
||||
@JsonProperty
|
||||
public String maxTokenCacheSize = "1048576";
|
||||
@JsonProperty
|
||||
public String truststore;
|
||||
@JsonProperty
|
||||
public String truststorePassword;
|
||||
@JsonProperty
|
||||
public String keystore;
|
||||
@JsonProperty
|
||||
public String keystorePassword;
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.infrastructure.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
/**
|
||||
* Mocks authentication by converting X-Auth-Token headers to X-Tenant-Ids.
|
||||
*/
|
||||
public class MockAuthenticationFilter implements Filter {
|
||||
private static final String X_AUTH_TOKEN_HEADER = "X-Auth-Token";
|
||||
|
||||
@Override
|
||||
public void destroy() {}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
final HttpServletRequest req = (HttpServletRequest) request;
|
||||
HttpServletRequestWrapper wrapper = requestWrapperFor(req);
|
||||
chain.doFilter(wrapper, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {}
|
||||
|
||||
/**
|
||||
* Returns an HttpServletRequestWrapper that serves tenant id headers from request attributes.
|
||||
*/
|
||||
private HttpServletRequestWrapper requestWrapperFor(final HttpServletRequest request) {
|
||||
return new HttpServletRequestWrapper(request) {
|
||||
@Override
|
||||
public Object getAttribute(String name) {
|
||||
if (name.equalsIgnoreCase(PostAuthenticationFilter.X_TENANT_ID_HEADER)) {
|
||||
String tenantId = request.getHeader(PostAuthenticationFilter.X_TENANT_ID_HEADER);
|
||||
return tenantId == null ? request.getHeader(X_AUTH_TOKEN_HEADER) : tenantId;
|
||||
}
|
||||
if (name.equalsIgnoreCase(PostAuthenticationFilter.X_IDENTITY_STATUS_ATTRIBUTE))
|
||||
return PostAuthenticationFilter.CONFIRMED_STATUS;
|
||||
if (name.equalsIgnoreCase(PostAuthenticationFilter.X_ROLES_ATTRIBUTE))
|
||||
return "user";
|
||||
return super.getAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(String name) {
|
||||
if (name.equalsIgnoreCase(PostAuthenticationFilter.X_TENANT_ID_HEADER))
|
||||
return request.getHeader(X_AUTH_TOKEN_HEADER);
|
||||
return super.getHeader(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getHeaderNames() {
|
||||
List<String> names = Collections.list(super.getHeaderNames());
|
||||
names.add(PostAuthenticationFilter.X_TENANT_ID_HEADER);
|
||||
return Collections.enumeration(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getHeaders(String name) {
|
||||
if (name.equalsIgnoreCase(PostAuthenticationFilter.X_TENANT_ID_HEADER)) {
|
||||
String authToken = request.getHeader(X_AUTH_TOKEN_HEADER);
|
||||
return authToken == null ? Collections.<String>emptyEnumeration() : Collections
|
||||
.enumeration(Collections.singleton(authToken));
|
||||
}
|
||||
return super.getHeaders(name);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.infrastructure.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import monasca.log.api.infrastructure.servlet.PreAuthenticationFilter.ErrorCapturingServletResponseWrapper;
|
||||
|
||||
/**
|
||||
* Authenticates requests using header information from the CsMiddleware. Provides the X-TENANT-ID
|
||||
* servlet attribute as a request header. Intended to be added to a servlet filter chain after the
|
||||
* CsMiddleware TokenAuth filter.
|
||||
*/
|
||||
public class PostAuthenticationFilter implements Filter {
|
||||
static final String CONFIRMED_STATUS = "CONFIRMED";
|
||||
static final String X_ROLES_ATTRIBUTE = "X-ROLES";
|
||||
static final String X_MONASCA_AGENT = "X-MONASCA_AGENT";
|
||||
static final String X_IDENTITY_STATUS_ATTRIBUTE = "X-IDENTITY-STATUS";
|
||||
private static final String X_TENANT_ID_ATTRIBUTE = "X-PROJECT-ID";
|
||||
static final String X_TENANT_ID_HEADER = "X-Tenant-Id";
|
||||
static final String X_ROLES_HEADER = "X-Roles";
|
||||
|
||||
private final List<String> defaultAuthorizedRoles = new ArrayList<String>();
|
||||
private final List<String> agentAuthorizedRoles = new ArrayList<String>();
|
||||
|
||||
public PostAuthenticationFilter(List<String> defaultAuthorizedRoles,
|
||||
List<String> agentAuthorizedRoles) {
|
||||
for (String defaultRole : defaultAuthorizedRoles) {
|
||||
this.defaultAuthorizedRoles.add(defaultRole.toLowerCase());
|
||||
}
|
||||
for (String agentRole : agentAuthorizedRoles) {
|
||||
this.agentAuthorizedRoles.add(agentRole.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
|
||||
final HttpServletRequest req = (HttpServletRequest) request;
|
||||
ErrorCapturingServletResponseWrapper res = (ErrorCapturingServletResponseWrapper) response;
|
||||
String tenantIdStr = null;
|
||||
|
||||
try {
|
||||
// According to CORS spec OPTIONS method does not pass auth info
|
||||
if (req.getMethod().equals("OPTIONS")) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
Object tenantId = request.getAttribute(X_TENANT_ID_ATTRIBUTE);
|
||||
|
||||
if (tenantId == null) {
|
||||
sendAuthError(res, null, null, null);
|
||||
return;
|
||||
}
|
||||
tenantIdStr = tenantId.toString();
|
||||
|
||||
boolean authenticated = isAuthenticated(req);
|
||||
boolean authorized = isAuthorized(req);
|
||||
|
||||
if (authenticated && authorized) {
|
||||
HttpServletRequestWrapper wrapper = requestWrapperFor(req);
|
||||
chain.doFilter(wrapper, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (authorized)
|
||||
sendAuthError(res, tenantIdStr, null, null);
|
||||
else
|
||||
sendAuthError(res, tenantIdStr, "Tenant is missing a required role to access this service",
|
||||
null);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
sendAuthError(res, tenantIdStr, null, e);
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {}
|
||||
|
||||
/**
|
||||
* @return true if the request is authenticated else false
|
||||
*/
|
||||
private boolean isAuthenticated(HttpServletRequest request) {
|
||||
Object identityStatus = request.getAttribute(X_IDENTITY_STATUS_ATTRIBUTE);
|
||||
return identityStatus != null && CONFIRMED_STATUS.equalsIgnoreCase(identityStatus.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the request is authorized else false
|
||||
*/
|
||||
private boolean isAuthorized(HttpServletRequest request) {
|
||||
Object rolesFromKeystone = request.getAttribute(X_ROLES_ATTRIBUTE);
|
||||
if (rolesFromKeystone == null)
|
||||
return false;
|
||||
|
||||
boolean agentUser = false;
|
||||
for (String role : rolesFromKeystone.toString().split(",")) {
|
||||
String lowerCaseRole = role.toLowerCase();
|
||||
if ((defaultAuthorizedRoles != null) && defaultAuthorizedRoles.contains(lowerCaseRole)) {
|
||||
return true;
|
||||
}
|
||||
if ((agentAuthorizedRoles != null) && agentAuthorizedRoles.contains(lowerCaseRole)) {
|
||||
agentUser = true;
|
||||
}
|
||||
}
|
||||
if (agentUser) {
|
||||
request.setAttribute(X_MONASCA_AGENT, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an HttpServletRequestWrapper that serves tenant id headers from request attributes.
|
||||
*/
|
||||
private HttpServletRequestWrapper requestWrapperFor(final HttpServletRequest request) {
|
||||
return new HttpServletRequestWrapper(request) {
|
||||
@Override
|
||||
public String getHeader(String name) {
|
||||
if (name.equalsIgnoreCase(X_TENANT_ID_HEADER))
|
||||
return request.getAttribute(X_TENANT_ID_ATTRIBUTE).toString();
|
||||
else if (name.equalsIgnoreCase(X_ROLES_HEADER))
|
||||
return request.getAttribute(X_ROLES_ATTRIBUTE).toString();
|
||||
return super.getHeader(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getHeaderNames() {
|
||||
List<String> names = Collections.list(super.getHeaderNames());
|
||||
names.add(X_TENANT_ID_HEADER);
|
||||
names.add(X_ROLES_HEADER);
|
||||
return Collections.enumeration(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getHeaders(String name) {
|
||||
if (name.equalsIgnoreCase(X_TENANT_ID_HEADER))
|
||||
return Collections.enumeration(Collections.singleton(request.getAttribute(
|
||||
X_TENANT_ID_ATTRIBUTE).toString()));
|
||||
else if (name.equalsIgnoreCase(X_ROLES_HEADER))
|
||||
return Collections.enumeration(Collections.singleton(request.getAttribute(
|
||||
X_ROLES_ATTRIBUTE).toString()));
|
||||
return super.getHeaders(name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void sendAuthError(ErrorCapturingServletResponseWrapper response,
|
||||
@Nullable String tenantId, @Nullable String message, @Nullable Exception exception)
|
||||
throws IOException {
|
||||
response.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
if (message == null)
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
|
||||
tenantId == null ? "Failed to authenticate request"
|
||||
: "Failed to authenticate request for " + tenantId, exception);
|
||||
else
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, String.format(message, tenantId));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.infrastructure.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import monasca.log.api.resource.exception.Exceptions;
|
||||
import monasca.log.api.resource.exception.Exceptions.FaultType;
|
||||
|
||||
/**
|
||||
* Authenticates requests using header information from the CsMiddleware. Provides the X-TENANT-ID
|
||||
* servlet attribute as a request header. Intended to be added to a servlet filter chain after the
|
||||
* CsMiddleware TokenAuth filter.
|
||||
*/
|
||||
public class PreAuthenticationFilter implements Filter {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PreAuthenticationFilter.class);
|
||||
|
||||
static class ErrorCapturingServletResponseWrapper extends HttpServletResponseWrapper {
|
||||
private int statusCode;
|
||||
private String errorMessage;
|
||||
private Exception exception;
|
||||
|
||||
public ErrorCapturingServletResponseWrapper(HttpServletResponse response) {
|
||||
super(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendError(int statusCode) throws IOException {
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendError(int statusCode, String msg) throws IOException {
|
||||
this.statusCode = statusCode;
|
||||
errorMessage = msg;
|
||||
}
|
||||
|
||||
void sendError(int statusCode, String msg, Exception exception) throws IOException {
|
||||
sendError(statusCode, msg);
|
||||
this.exception = exception;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
|
||||
HttpServletResponse res = (HttpServletResponse) response;
|
||||
ErrorCapturingServletResponseWrapper responseWrapper =
|
||||
new ErrorCapturingServletResponseWrapper(res);
|
||||
|
||||
boolean caughtException = false;
|
||||
ServletOutputStream out = null;
|
||||
try {
|
||||
out = res.getOutputStream();
|
||||
chain.doFilter(request, responseWrapper);
|
||||
if (responseWrapper.statusCode != 401 && responseWrapper.statusCode != 500)
|
||||
return;
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error while executing pre authentication filter", e);
|
||||
caughtException = true;
|
||||
}
|
||||
|
||||
try {
|
||||
res.setContentType(MediaType.APPLICATION_JSON);
|
||||
if (caughtException) {
|
||||
res.setStatus(Response.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
else {
|
||||
res.setStatus(responseWrapper.statusCode);
|
||||
FaultType faultType;
|
||||
if (responseWrapper.statusCode == 500) {
|
||||
faultType = FaultType.SERVER_ERROR;
|
||||
}
|
||||
else {
|
||||
faultType = FaultType.UNAUTHORIZED;
|
||||
}
|
||||
String output = Exceptions.buildLoggedErrorMessage(faultType, responseWrapper.errorMessage,
|
||||
null, responseWrapper.exception);
|
||||
out.print(output);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// CSMiddleware is throwing this error for invalid tokens.
|
||||
// This problem appears to be fixed in other versions, but they are not approved yet.
|
||||
try {
|
||||
String output =
|
||||
Exceptions.buildLoggedErrorMessage(FaultType.UNAUTHORIZED, "invalid authToken", null,
|
||||
responseWrapper.exception);
|
||||
out.print(output);
|
||||
} catch (Exception x) {
|
||||
LOG.error("Error while writing failed authentication HTTP response", x);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error while writing failed authentication HTTP response", e);
|
||||
} finally {
|
||||
if (out != null)
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package monasca.log.api.infrastructure.servlet;
|
||||
|
||||
import monasca.common.middleware.AuthConstants;
|
||||
import monasca.log.api.resource.exception.Exceptions;
|
||||
|
||||
import com.sun.jersey.spi.container.ContainerRequest;
|
||||
import com.sun.jersey.spi.container.ContainerRequestFilter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.core.Context;
|
||||
|
||||
import static monasca.log.api.infrastructure.servlet.PostAuthenticationFilter.X_MONASCA_AGENT;
|
||||
|
||||
public class RoleAuthorizationFilter implements ContainerRequestFilter {
|
||||
private static final Logger logger = LoggerFactory.getLogger
|
||||
(ContainerRequestFilter.class);
|
||||
@Context
|
||||
private HttpServletRequest httpServletRequest;
|
||||
private static final String[] VALID_MONASCA_AGENT_POST_PATHS = new String[] { "/v2.0/metrics", "/v2.0/log/single" };
|
||||
private static final String[] VALID_MONASCA_AGENT_GET_PATHS = new String[] { "/", "/v2.0" };
|
||||
|
||||
@Override
|
||||
public ContainerRequest filter(ContainerRequest containerRequest) {
|
||||
String method = containerRequest.getMethod();
|
||||
Object isAgent = httpServletRequest.getAttribute(X_MONASCA_AGENT);
|
||||
String pathInfo = httpServletRequest.getPathInfo();
|
||||
|
||||
// X_MONASCA_AGENT is only set if the only valid role for this user is an agent role
|
||||
if (isAgent != null) {
|
||||
if (!(method.equals("POST") && validPath(pathInfo, VALID_MONASCA_AGENT_POST_PATHS)) &&
|
||||
!(method.equals("GET") && validPath(pathInfo, VALID_MONASCA_AGENT_GET_PATHS))) {
|
||||
logger.warn("User {} is missing a required role to {} on {}",
|
||||
httpServletRequest.getAttribute(AuthConstants.AUTH_USER_NAME),
|
||||
method, pathInfo);
|
||||
throw Exceptions.badRequest("User is missing a required role to perform this request");
|
||||
}
|
||||
}
|
||||
return containerRequest;
|
||||
}
|
||||
|
||||
private boolean validPath(String pathInfo, String[] paths) {
|
||||
// Make the comparison easier by getting rid of trailing slashes
|
||||
while (!pathInfo.isEmpty() && !"/".equals(pathInfo) && pathInfo.endsWith("/")) {
|
||||
pathInfo = pathInfo.substring(0, pathInfo.length() - 1);
|
||||
}
|
||||
for (final String validPath : paths) {
|
||||
if (validPath.equals(pathInfo)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright 2015 FUJITSU LIMITED
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
package monasca.log.api.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a log.
|
||||
*/
|
||||
public class Log
|
||||
implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 685205295808136758L;
|
||||
|
||||
public String applicationType;
|
||||
public Map<String, String> dimensions;
|
||||
public String message;
|
||||
|
||||
public Log() {
|
||||
}
|
||||
|
||||
public Log(@Nullable String applicationType, @Nullable Map<String, String> dimensions,
|
||||
String message) {
|
||||
this.applicationType = applicationType;
|
||||
this.dimensions = dimensions;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Log{" + "applicationType='" + applicationType + '\'' + ", dimensions=" + dimensions
|
||||
+ ", message=" + message + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (!(obj instanceof Log))
|
||||
return false;
|
||||
Log other = (Log) obj;
|
||||
if (dimensions == null) {
|
||||
if (other.dimensions != null)
|
||||
return false;
|
||||
} else if (!dimensions.equals(other.dimensions))
|
||||
return false;
|
||||
// internally handles null and empty strings as same for applicationType
|
||||
if (applicationType == null) {
|
||||
if (other.applicationType != null && !other.applicationType.isEmpty())
|
||||
return false;
|
||||
} else if (applicationType.isEmpty()) {
|
||||
if (other.applicationType != null && !other.applicationType.isEmpty())
|
||||
return false;
|
||||
} else if (!applicationType.equals(other.applicationType))
|
||||
return false;
|
||||
if (!message.equals(other.message))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((dimensions == null) ? 0 : dimensions.hashCode());
|
||||
result = prime * result + ((applicationType == null) ? 0 : applicationType.hashCode());
|
||||
result = prime * result + message.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getApplicationType() {
|
||||
return applicationType;
|
||||
}
|
||||
|
||||
public void setApplicationType(String applicationType) {
|
||||
this.applicationType = applicationType;
|
||||
}
|
||||
|
||||
public Map<String, String> getDimensions() {
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
public void setDimensions(Map<String, String> dimensions) {
|
||||
this.dimensions = dimensions;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2015 FUJITSU LIMITED
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
package monasca.log.api.model;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
* A log envelope.
|
||||
*/
|
||||
public class LogEnvelope {
|
||||
public Log log;
|
||||
public Map<String, Object> meta;
|
||||
public long creationTime;
|
||||
|
||||
protected LogEnvelope() {
|
||||
}
|
||||
|
||||
public LogEnvelope(Log log) {
|
||||
Preconditions.checkNotNull(log, "log");
|
||||
this.log = log;
|
||||
this.creationTime = System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
public LogEnvelope(Log log, Map<String, Object> meta) {
|
||||
this(log);
|
||||
Preconditions.checkNotNull(meta, "meta");
|
||||
this.meta = meta;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2015 FUJITSU LIMITED
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
package monasca.log.api.model;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import monasca.common.util.Exceptions;
|
||||
|
||||
/**
|
||||
* Utilities for working with LogEnvelopes.
|
||||
*/
|
||||
public final class LogEnvelopes {
|
||||
private LogEnvelopes() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LogEnvelope for the {@code logJson}.
|
||||
*
|
||||
* @throws RuntimeException if an error occurs while parsing {@code logJson}
|
||||
*/
|
||||
public static LogEnvelope fromJson(byte[] logJson) {
|
||||
try {
|
||||
String jsonStr = new String(logJson, "UTF-8");
|
||||
return Logs.OBJECT_MAPPER.readValue(jsonStr, LogEnvelope.class);
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.uncheck(e, "Failed to parse log json: %s", new String(logJson));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON representation of the {@code envelope} else null if it could not be converted
|
||||
* to JSON.
|
||||
*/
|
||||
public static String toJson(LogEnvelope envelope) {
|
||||
try {
|
||||
return Logs.OBJECT_MAPPER.writeValueAsString(envelope);
|
||||
} catch (JsonProcessingException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright 2015 FUJITSU LIMITED
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
package monasca.log.api.model;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import monasca.common.util.Exceptions;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
|
||||
/**
|
||||
* Utilities for working with Logs.
|
||||
*/
|
||||
public final class Logs {
|
||||
static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
static {
|
||||
OBJECT_MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
|
||||
SimpleModule module = new SimpleModule();
|
||||
module.addSerializer(new LogSerializer());
|
||||
OBJECT_MAPPER.registerModule(module);
|
||||
}
|
||||
|
||||
private Logs() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Log for the {@code logJson}.
|
||||
*
|
||||
* @throws RuntimeException if an error occurs while parsing {@code logJson}
|
||||
*/
|
||||
public static Log fromJson(byte[] logJson) {
|
||||
try {
|
||||
String jsonStr = StringEscapeUtils.unescapeJava(new String(logJson, "UTF-8"));
|
||||
return OBJECT_MAPPER.readValue(jsonStr, Log.class);
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.uncheck(e, "Failed to parse log json: %s", new String(logJson));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON representation of the {@code log} else null if it could not be converted to
|
||||
* JSON.
|
||||
*/
|
||||
public static String toJson(Log log) {
|
||||
try {
|
||||
return OBJECT_MAPPER.writeValueAsString(log);
|
||||
} catch (JsonProcessingException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Log serializer */
|
||||
private static class LogSerializer
|
||||
extends JsonSerializer<Log> {
|
||||
@Override
|
||||
public Class<Log> handledType() {
|
||||
return Log.class;
|
||||
}
|
||||
|
||||
public void serialize(Log log, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException, JsonProcessingException {
|
||||
jgen.writeStartObject();
|
||||
|
||||
if (log.applicationType != null && !log.applicationType.isEmpty()) {
|
||||
jgen.writeStringField("application_type", log.applicationType);
|
||||
}
|
||||
if (log.dimensions != null && !log.dimensions.isEmpty()) {
|
||||
jgen.writeObjectField("dimensions", log.dimensions);
|
||||
}
|
||||
jgen.writeStringField("message", log.message);
|
||||
|
||||
jgen.writeEndObject();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2015 Fujitsu Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.resource;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import monasca.log.api.app.LogService;
|
||||
import monasca.log.api.app.command.CreateLogCommand;
|
||||
import monasca.log.api.app.validation.Validation;
|
||||
import monasca.log.api.resource.exception.Exceptions;
|
||||
|
||||
/**
|
||||
* Log resource implementation.
|
||||
*/
|
||||
@Path("/v2.0/log")
|
||||
public class LogResource {
|
||||
private static final String MONITORING_DELEGATE_ROLE = "monitoring-delegate";
|
||||
private static final Splitter COMMA_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults();
|
||||
|
||||
private final LogService service;
|
||||
|
||||
@Inject
|
||||
public LogResource(LogService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
|
||||
@Path("/single")
|
||||
public void single(@Context UriInfo uriInfo,
|
||||
@HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@HeaderParam("X-Roles") String roles,
|
||||
@HeaderParam("X-Application-Type") String applicationType,
|
||||
@HeaderParam("X-Dimensions") String dimensionsStr,
|
||||
@QueryParam("tenant_id") String crossTenantId, String message) {
|
||||
|
||||
boolean isDelegate = !Strings.isNullOrEmpty(roles) && COMMA_SPLITTER.splitToList(roles).contains(MONITORING_DELEGATE_ROLE);
|
||||
|
||||
Map<String, String> dimensions = Strings.isNullOrEmpty(dimensionsStr) ? null : Validation.parseLogDimensions(dimensionsStr);
|
||||
|
||||
CreateLogCommand command = new CreateLogCommand(applicationType, dimensions, message);
|
||||
|
||||
if (!isDelegate) {
|
||||
if (!Strings.isNullOrEmpty(crossTenantId)) {
|
||||
throw Exceptions.forbidden("Project %s cannot POST cross tenant metrics", tenantId);
|
||||
}
|
||||
}
|
||||
|
||||
command.validate();
|
||||
|
||||
service.create(command.toLog(), Strings.isNullOrEmpty(crossTenantId) ? tenantId : crossTenantId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.resource.exception;
|
||||
|
||||
import io.dropwizard.jersey.validation.ValidationErrorMessage;
|
||||
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
import monasca.log.api.resource.exception.Exceptions.FaultType;
|
||||
|
||||
@Provider
|
||||
public class ConstraintViolationExceptionMapper implements
|
||||
ExceptionMapper<ConstraintViolationException> {
|
||||
private static final int UNPROCESSABLE_ENTITY = 422;
|
||||
|
||||
@Override
|
||||
public Response toResponse(ConstraintViolationException exception) {
|
||||
final ValidationErrorMessage message =
|
||||
new ValidationErrorMessage(exception.getConstraintViolations());
|
||||
String msg =
|
||||
message.getErrors().isEmpty() ? exception.getMessage() : message.getErrors().toString();
|
||||
return Response.status(UNPROCESSABLE_ENTITY).type(MediaType.APPLICATION_JSON)
|
||||
.entity(Exceptions.buildLoggedErrorMessage(FaultType.UNPROCESSABLE_ENTITY, msg)).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.resource.exception;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
public class ErrorMessage {
|
||||
public int code;
|
||||
public String message;
|
||||
public String details;
|
||||
@JsonProperty("internal_code")
|
||||
public String internalCode;
|
||||
|
||||
ErrorMessage() {}
|
||||
|
||||
public ErrorMessage(int code, String message, String details, String internalCode) {
|
||||
Preconditions.checkNotNull(internalCode, "internalCode");
|
||||
|
||||
this.code = code;
|
||||
this.message = message == null ? "" : message;
|
||||
this.details = details == null ? "" : details;
|
||||
this.internalCode = internalCode;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.resource.exception;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.google.common.base.Splitter;
|
||||
|
||||
/**
|
||||
* Exception factory methods.
|
||||
*/
|
||||
public final class Exceptions {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Exceptions.class);
|
||||
private static final ObjectMapper OBJECT_MAPPER;
|
||||
private static final Splitter LINE_SPLITTER = Splitter.on("\n").trimResults();
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
static {
|
||||
OBJECT_MAPPER = new ObjectMapper();
|
||||
OBJECT_MAPPER
|
||||
.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
|
||||
}
|
||||
|
||||
public enum FaultType {
|
||||
|
||||
SERVER_ERROR(Status.INTERNAL_SERVER_ERROR, true),
|
||||
BAD_REQUEST(Status.BAD_REQUEST, true),
|
||||
UNAUTHORIZED(Status.UNAUTHORIZED, false),
|
||||
NOT_FOUND(Status.NOT_FOUND, true),
|
||||
CONFLICT(Status.CONFLICT, true),
|
||||
UNPROCESSABLE_ENTITY(422, true),
|
||||
FORBIDDEN(Status.FORBIDDEN, true);
|
||||
|
||||
public final int statusCode;
|
||||
public final boolean loggable;
|
||||
|
||||
FaultType(int statusCode, boolean loggable) {
|
||||
this.statusCode = statusCode;
|
||||
this.loggable = loggable;
|
||||
}
|
||||
|
||||
FaultType(Status status, boolean loggable) {
|
||||
this.statusCode = status.getStatusCode();
|
||||
this.loggable = loggable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
private static class WebAppException extends WebApplicationException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public WebAppException(FaultType faultType, String message) {
|
||||
super(Response.status(faultType.statusCode).entity(message).type(MediaType.APPLICATION_JSON)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
private Exceptions() {}
|
||||
|
||||
public static WebApplicationException badRequest(String msg, Object... args) {
|
||||
return new WebAppException(FaultType.BAD_REQUEST, buildLoggedErrorMessage(
|
||||
FaultType.BAD_REQUEST, msg, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns an error message containing an error code, and logs the message with the
|
||||
* corresponding error code.
|
||||
*/
|
||||
public static String buildLoggedErrorMessage(FaultType faultType, String message, Object... args) {
|
||||
return buildLoggedErrorMessage(faultType,
|
||||
args == null || args.length == 0 ? message : String.format(message, args), null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns an error message containing an error code, and logs the message with the
|
||||
* corresponding error code.
|
||||
*/
|
||||
public static String buildLoggedErrorMessage(FaultType faultType, String message,
|
||||
@Nullable String details, @Nullable Throwable exception) {
|
||||
String errorCode = Long.toHexString(RANDOM.nextLong());
|
||||
|
||||
if (faultType.loggable) {
|
||||
String withoutDetails = "{} {} - {}";
|
||||
String withDetails = "{} {} - {} {}";
|
||||
|
||||
if (details == null) {
|
||||
if (exception == null)
|
||||
LOG.error(withoutDetails, faultType.name(), errorCode, message);
|
||||
else
|
||||
LOG.error(withoutDetails, faultType.name(), errorCode, message, exception);
|
||||
} else {
|
||||
if (exception == null)
|
||||
LOG.error(withDetails, faultType.name(), errorCode, message, details);
|
||||
else
|
||||
LOG.error(withDetails, faultType.name(), errorCode, message, details, exception);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
StringBuilder str = new StringBuilder("{\"");
|
||||
str.append(faultType.toString());
|
||||
str.append("\":");
|
||||
str.append(OBJECT_MAPPER.writeValueAsString(new ErrorMessage(faultType.statusCode, message,
|
||||
details, errorCode)));
|
||||
str.append("}");
|
||||
return str.toString();
|
||||
} catch (JsonProcessingException bestEffort) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static WebApplicationException forbidden(String msg, Object... args) {
|
||||
return new WebAppException(FaultType.FORBIDDEN, buildLoggedErrorMessage(FaultType.FORBIDDEN,
|
||||
msg, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first line off of a stacktrace message.
|
||||
*/
|
||||
public static String stripLocationFromStacktrace(String message) {
|
||||
for (String s : LINE_SPLITTER.split(message))
|
||||
return s;
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the content of a a POSTed request entity is invalid.
|
||||
*/
|
||||
public static WebApplicationException unprocessableEntity(String msg, Object... args) {
|
||||
return new WebAppException(FaultType.UNPROCESSABLE_ENTITY, buildLoggedErrorMessage(
|
||||
FaultType.UNPROCESSABLE_ENTITY, msg, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the content of a a POSTed request entity is invalid.
|
||||
*/
|
||||
public static WebApplicationException unprocessableEntityDetails(String msg, String details,
|
||||
Exception exception) {
|
||||
return new WebAppException(FaultType.UNPROCESSABLE_ENTITY, buildLoggedErrorMessage(
|
||||
FaultType.UNPROCESSABLE_ENTITY, msg, details, exception));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.resource.exception;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
import monasca.log.api.resource.exception.Exceptions.FaultType;
|
||||
|
||||
@Provider
|
||||
public class IllegalArgumentExceptionMapper implements ExceptionMapper<IllegalArgumentException> {
|
||||
@Override
|
||||
public Response toResponse(IllegalArgumentException e) {
|
||||
return Response.status(Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON)
|
||||
.entity(Exceptions.buildLoggedErrorMessage(FaultType.BAD_REQUEST, e.getMessage())).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.resource.exception;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
|
||||
import monasca.log.api.resource.exception.Exceptions.FaultType;
|
||||
|
||||
/**
|
||||
* Adapted from Dropwizard's JsonMappingExceptionManager.
|
||||
*/
|
||||
@Provider
|
||||
public class JsonMappingExceptionManager implements ExceptionMapper<JsonMappingException> {
|
||||
@Override
|
||||
public Response toResponse(JsonMappingException exception) {
|
||||
return Response
|
||||
.status(FaultType.BAD_REQUEST.statusCode)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.entity(
|
||||
Exceptions.buildLoggedErrorMessage(FaultType.BAD_REQUEST,
|
||||
"Unable to process the provided JSON",
|
||||
Exceptions.stripLocationFromStacktrace(exception.getMessage()), null)).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.resource.exception;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerationException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
||||
import monasca.log.api.resource.exception.Exceptions.FaultType;
|
||||
|
||||
/**
|
||||
* Adapted from Dropwizard's JsonProcessingExceptionMapper.
|
||||
*/
|
||||
@Provider
|
||||
public class JsonProcessingExceptionMapper implements ExceptionMapper<JsonProcessingException> {
|
||||
@Override
|
||||
public Response toResponse(JsonProcessingException exception) {
|
||||
/*
|
||||
* If the error is in the JSON generation, it's a server error.
|
||||
*/
|
||||
if (exception instanceof JsonGenerationException)
|
||||
return Response
|
||||
.status(Status.INTERNAL_SERVER_ERROR)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.entity(
|
||||
Exceptions.buildLoggedErrorMessage(FaultType.SERVER_ERROR, "Error generating JSON",
|
||||
null, exception)).build();
|
||||
|
||||
final String message = exception.getMessage();
|
||||
|
||||
/*
|
||||
* If we can't deserialize the JSON because someone forgot a no-arg constructor, it's a server
|
||||
* error and we should inform the developer.
|
||||
*/
|
||||
if (message.startsWith("No suitable constructor found"))
|
||||
return Response
|
||||
.status(Status.INTERNAL_SERVER_ERROR)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.entity(
|
||||
Exceptions.buildLoggedErrorMessage(FaultType.SERVER_ERROR,
|
||||
"Unable to deserialize the provided JSON", null, exception)).build();
|
||||
|
||||
/*
|
||||
* Otherwise, it's those pesky users.
|
||||
*/
|
||||
return Response
|
||||
.status(Status.BAD_REQUEST)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.entity(
|
||||
Exceptions.buildLoggedErrorMessage(FaultType.BAD_REQUEST,
|
||||
"Unable to process the provided JSON",
|
||||
Exceptions.stripLocationFromStacktrace(message), exception)).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.resource.exception;
|
||||
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
import monasca.log.api.resource.exception.Exceptions.FaultType;
|
||||
|
||||
/**
|
||||
* Adapted from Dropwizard's LoggingExceptionMapper.
|
||||
*
|
||||
* @param <E> Exception type
|
||||
*/
|
||||
@Provider
|
||||
public class ThrowableExceptionMapper<E extends Throwable> implements ExceptionMapper<E> {
|
||||
@Override
|
||||
public Response toResponse(E exception) {
|
||||
if (exception instanceof WebApplicationException)
|
||||
return ((WebApplicationException) exception).getResponse();
|
||||
|
||||
return Response
|
||||
.status(Status.INTERNAL_SERVER_ERROR)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.entity(
|
||||
Exceptions.buildLoggedErrorMessage(FaultType.SERVER_ERROR,
|
||||
"An internal server error occurred", null, exception)).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package monasca.log.api;
|
||||
|
||||
import monasca.log.api.MonApiApplication;
|
||||
|
||||
public class MonApiApplicationRunner {
|
||||
public static void main(String... args) throws Exception {
|
||||
MonApiApplication.main(new String[] {"server", "config-local.yml"});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2015 Fujitsu Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.app;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import kafka.javaapi.producer.Producer;
|
||||
import kafka.producer.KeyedMessage;
|
||||
|
||||
import org.mockito.Mockito;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import monasca.log.api.ApiConfig;
|
||||
import monasca.log.api.model.Log;
|
||||
|
||||
@Test
|
||||
public class LogServiceTest {
|
||||
private final String APPLICATION_TYPE = "Application/Json";
|
||||
private final Map<String, String> dimensions = new HashMap<String, String>();
|
||||
private final String MESSAGE = "message";
|
||||
private final String TENANT_ID = "Fujitsu";
|
||||
private final String TOPIC = "topic";
|
||||
private final String REGION = "region";
|
||||
|
||||
private ApiConfig config;
|
||||
private Producer<String, String> producer;
|
||||
LogService logService;
|
||||
|
||||
@BeforeMethod
|
||||
protected void beforeMethod() {
|
||||
|
||||
dimensions.clear();
|
||||
dimensions.put("a", "b");
|
||||
config = new ApiConfig();
|
||||
config.region = REGION;
|
||||
config.logTopic = TOPIC;
|
||||
producer = Mockito.mock(Producer.class);
|
||||
}
|
||||
|
||||
public void testCreate() {
|
||||
String key = "FujitsuApplication/Jsonab";
|
||||
String date = Long.toString(new Date().getTime()).substring(0, 10);
|
||||
String json = "{\"log\":{\"application_type\":\"Application/Json\",\"dimensions\":{\"a\":\"b\"},\"message\":\"message\"},\"meta\":{\"tenantId\":\"Fujitsu\",\"region\":\"region\"},\"creation_time\":"+ date +"}";
|
||||
KeyedMessage<String, String> keyMessage = new KeyedMessage<>("topic", key, json);
|
||||
Log log = new Log(APPLICATION_TYPE, dimensions, MESSAGE);
|
||||
logService = new LogService(config, producer);
|
||||
logService.create(log, TENANT_ID);
|
||||
Mockito.verify(producer, Mockito.times(1)).send(keyMessage);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright 2015 Fujitsu Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.app.command;
|
||||
|
||||
import static org.testng.AssertJUnit.assertEquals;
|
||||
import static org.testng.AssertJUnit.assertFalse;
|
||||
import static org.testng.AssertJUnit.assertTrue;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import monasca.log.api.app.command.CreateLogCommand;
|
||||
import monasca.log.api.model.Log;
|
||||
|
||||
@Test
|
||||
public class CreateLogCommandTest {
|
||||
private final String APPLICATION_TYPE = "Application/Json";
|
||||
private final Map<String, String> dimensions = new HashMap<String, String>();
|
||||
private final String MESSAGE = "message";
|
||||
CreateLogCommand createLogCommand;
|
||||
|
||||
@BeforeMethod
|
||||
protected void beforeMethod() {
|
||||
dimensions.clear();
|
||||
dimensions.put("a", "b");
|
||||
createLogCommand = new CreateLogCommand(APPLICATION_TYPE, dimensions, MESSAGE);
|
||||
}
|
||||
|
||||
public void testHashEquals() {
|
||||
CreateLogCommand createLogCommandTmp1 = new CreateLogCommand(APPLICATION_TYPE, dimensions, MESSAGE);
|
||||
CreateLogCommand createLogCommandTmp2 = new CreateLogCommand(APPLICATION_TYPE, dimensions, "");
|
||||
CreateLogCommand createLogCommandTmp3 = new CreateLogCommand(APPLICATION_TYPE, dimensions, null);
|
||||
CreateLogCommand createLogCommandTmp4 = new CreateLogCommand(APPLICATION_TYPE, null, MESSAGE);
|
||||
dimensions.clear();
|
||||
dimensions.put("1", "2");
|
||||
CreateLogCommand createLogCommandTmp5 = new CreateLogCommand(APPLICATION_TYPE, dimensions, MESSAGE);
|
||||
CreateLogCommand createLogCommandTmp6 = new CreateLogCommand(null, dimensions, MESSAGE);
|
||||
CreateLogCommand createLogCommandTmp7 = new CreateLogCommand("", dimensions, MESSAGE);
|
||||
|
||||
assertFalse(createLogCommand.equals(new String()));
|
||||
assertFalse(createLogCommand.equals(null));
|
||||
assertTrue(createLogCommand.equals(createLogCommand));
|
||||
assertTrue(createLogCommand.equals(createLogCommandTmp1));
|
||||
assertFalse(createLogCommand.equals(createLogCommandTmp2));
|
||||
assertFalse(createLogCommand.equals(createLogCommandTmp3));
|
||||
assertFalse(createLogCommand.equals(createLogCommandTmp4));
|
||||
assertFalse(createLogCommand.equals(createLogCommandTmp5));
|
||||
assertFalse(createLogCommand.equals(createLogCommandTmp6));
|
||||
assertFalse(createLogCommand.equals(createLogCommandTmp7));
|
||||
|
||||
assertEquals(createLogCommand.hashCode(), -446078499);
|
||||
|
||||
createLogCommand = new CreateLogCommand();
|
||||
createLogCommand.message = "a";
|
||||
assertFalse(createLogCommand.equals(new CreateLogCommand(APPLICATION_TYPE, null, "a")));
|
||||
assertFalse(createLogCommand.equals(new CreateLogCommand(null, dimensions, "a")));
|
||||
assertTrue(createLogCommand.equals(new CreateLogCommand(null, null, "a")));
|
||||
assertEquals(createLogCommand.hashCode(), 2889727);
|
||||
assertFalse(new CreateLogCommand(APPLICATION_TYPE, dimensions, "a").equals(new CreateLogCommand("", dimensions, "a")));
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void testSetDimensions() {
|
||||
Map<String, String> validatedDimensions = new HashMap<String, String>();
|
||||
validatedDimensions.put("1", "2");
|
||||
dimensions.clear();
|
||||
dimensions.put(" 1", "2 ");
|
||||
createLogCommand.setDimensions(dimensions);
|
||||
assertEquals(createLogCommand.dimensions, validatedDimensions);
|
||||
}
|
||||
|
||||
public void testSetApplicationType() {
|
||||
createLogCommand.setApplicationType(" aa ");
|
||||
assertEquals(createLogCommand.applicationType, "aa");
|
||||
}
|
||||
|
||||
public void testToLog() {
|
||||
Log log = new Log(APPLICATION_TYPE, dimensions, MESSAGE);
|
||||
assertEquals(createLogCommand.toLog(), log);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = Exception.class)
|
||||
public void testValidate() {
|
||||
createLogCommand.validate();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2015 Fujitsu Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package monasca.log.api.app.validation;
|
||||
|
||||
import static org.testng.AssertJUnit.assertEquals;
|
||||
import static org.testng.AssertJUnit.assertNull;
|
||||
import static org.testng.AssertJUnit.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
|
||||
import monasca.log.api.app.validation.LogApplicationTypeValidator;
|
||||
|
||||
@Test
|
||||
public class LogApplicationTypeValidationTest {
|
||||
|
||||
private final String toNormalize = " Json Application ";
|
||||
private final String normalized = "Json Application";
|
||||
|
||||
public void testNormalize() {
|
||||
|
||||
String result = LogApplicationTypeValidator.normalize(toNormalize);
|
||||
assertEquals(normalized, result);
|
||||
result = LogApplicationTypeValidator.normalize(null);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
public void testValidateWrongFormat() throws JsonParseException, IOException {
|
||||
int exceptionCount = 0;
|
||||
try {
|
||||
LogApplicationTypeValidator.validate(normalized);
|
||||
} catch (WebApplicationException e) {
|
||||
exceptionCount++;
|
||||
String msg = getMessage((String) e.getResponse().getEntity());
|
||||
assertEquals(msg, "Application type Json Application may only contain: a-z A-Z 0-9 _ - .");
|
||||
}
|
||||
|
||||
assertEquals("Method throws Exception with correct message", 1, exceptionCount);
|
||||
}
|
||||
|
||||
public void testValidateWrongLength() throws JsonParseException, IOException {
|
||||
StringBuilder message = new StringBuilder();
|
||||
int exceptionCount = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
message.append('a');
|
||||
}
|
||||
try {
|
||||
LogApplicationTypeValidator.validate(message.toString());
|
||||
} catch (WebApplicationException e) {
|
||||
exceptionCount++;
|
||||
String msg = getMessage((String) e.getResponse().getEntity());
|
||||
assertTrue(msg.contains("must be 255 characters or less"));
|
||||
}
|
||||
assertEquals("Method throws Exception with correct message", 1, exceptionCount);
|
||||
}
|
||||
|
||||
private String getMessage(String json) throws JsonParseException, IOException {
|
||||
JsonFactory factory = new JsonFactory();
|
||||
JsonParser jp = factory.createParser(json);
|
||||
jp.nextToken();
|
||||
while (jp.nextToken() != JsonToken.END_OBJECT) {
|
||||
String fieldname = jp.getCurrentName();
|
||||
jp.nextToken();
|
||||
if ("message".equals(fieldname)) {
|
||||
|
||||
return jp.getText();
|
||||
}
|
||||
}
|
||||
jp.close();
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* Copyright 2015 FUJITSU LIMITED
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
package monasca.log.api.model;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test
|
||||
public class LogTest {
|
||||
public void shouldSerializeValue() {
|
||||
String applicationType = "apache";
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "WebService01");
|
||||
dimensions.put("environment", "production");
|
||||
String message = "Hello, world!";
|
||||
Log log = new Log(applicationType, dimensions, message);
|
||||
String json = Logs.toJson(log);
|
||||
assertEquals(
|
||||
json,
|
||||
"{\"application_type\":\"apache\",\"dimensions\":{\"app_name\":\"WebService01\",\"environment\":\"production\"}," +
|
||||
"\"message\":\"Hello, world!\"}");
|
||||
}
|
||||
|
||||
public void shouldSerializeValueWithNull() {
|
||||
String applicationType = null;
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "WebService01");
|
||||
dimensions.put("environment", "production");
|
||||
String message = "Hello, world!";
|
||||
Log log = new Log(applicationType, dimensions, message);
|
||||
String json = Logs.toJson(log);
|
||||
assertEquals(
|
||||
json,
|
||||
"{\"dimensions\":{\"app_name\":\"WebService01\",\"environment\":\"production\"}," +
|
||||
"\"message\":\"Hello, world!\"}");
|
||||
}
|
||||
|
||||
public void shouldSerializeValueWithNull_2() {
|
||||
String applicationType = "apache";
|
||||
SortedMap<String, String> dimensions = null;
|
||||
String message = "Hello, world!";
|
||||
Log log = new Log(applicationType, dimensions, message);
|
||||
String json = Logs.toJson(log);
|
||||
assertEquals(
|
||||
json,
|
||||
"{\"application_type\":\"apache\"," + "\"message\":\"Hello, world!\"}");
|
||||
}
|
||||
|
||||
public void shouldSerializeValueWithNull_3() {
|
||||
String applicationType = null;
|
||||
SortedMap<String, String> dimensions = null;
|
||||
String message = "Hello, world!";
|
||||
Log log = new Log(applicationType, dimensions, message);
|
||||
String json = Logs.toJson(log);
|
||||
assertEquals(
|
||||
json,
|
||||
"{\"message\":\"Hello, world!\"}");
|
||||
}
|
||||
|
||||
public void shouldSerializeValueWithEmpty() {
|
||||
String applicationType = "";
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "WebService01");
|
||||
dimensions.put("environment", "production");
|
||||
String message = "Hello, world!";
|
||||
Log log = new Log(applicationType, dimensions, message);
|
||||
String json = Logs.toJson(log);
|
||||
assertEquals(
|
||||
json,
|
||||
"{\"dimensions\":{\"app_name\":\"WebService01\",\"environment\":\"production\"}," +
|
||||
"\"message\":\"Hello, world!\"}");
|
||||
}
|
||||
|
||||
public void shouldSerializeValueWithEmpty_2() {
|
||||
String applicationType = "apache";
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "WebService01");
|
||||
dimensions.put("environment", "production");
|
||||
String message = "";
|
||||
Log log = new Log(applicationType, dimensions, message);
|
||||
String json = Logs.toJson(log);
|
||||
assertEquals(
|
||||
json,
|
||||
"{\"application_type\":\"apache\",\"dimensions\":{\"app_name\":\"WebService01\",\"environment\":\"production\"}," +
|
||||
"\"message\":\"\"}");
|
||||
}
|
||||
|
||||
public void shouldSerializeValueWithEmpty_3() {
|
||||
String applicationType = "";
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "WebService01");
|
||||
dimensions.put("environment", "production");
|
||||
String message = "";
|
||||
Log log = new Log(applicationType, dimensions, message);
|
||||
String json = Logs.toJson(log);
|
||||
assertEquals(
|
||||
json,
|
||||
"{\"dimensions\":{\"app_name\":\"WebService01\",\"environment\":\"production\"}," +
|
||||
"\"message\":\"\"}");
|
||||
}
|
||||
|
||||
public void shouldSerializeAndDeserialize() {
|
||||
String applicationType = "apache";
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "WebService01");
|
||||
dimensions.put("environment", "production");
|
||||
dimensions.put("instance_id", "123");
|
||||
String message = "Hello, world!";
|
||||
Log expected = new Log(applicationType, dimensions, message);
|
||||
|
||||
Log log = Logs.fromJson(Logs.toJson(expected).getBytes());
|
||||
assertEquals(log, expected);
|
||||
}
|
||||
|
||||
public void shouldSerializeAndDeserializeWithNull() {
|
||||
String applicationType = null;
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "WebService01");
|
||||
dimensions.put("environment", "production");
|
||||
dimensions.put("instance_id", "123");
|
||||
String message = "Hello, world!";
|
||||
Log expected = new Log(applicationType, dimensions, message);
|
||||
|
||||
Log log = Logs.fromJson(Logs.toJson(expected).getBytes());
|
||||
assertEquals(log, expected);
|
||||
}
|
||||
|
||||
public void shouldSerializeAndDeserializeWithNull_2() {
|
||||
String applicationType = "apache";
|
||||
SortedMap<String, String> dimensions = null;
|
||||
String message = "Hello, world!";
|
||||
Log expected = new Log(applicationType, dimensions, message);
|
||||
|
||||
Log log = Logs.fromJson(Logs.toJson(expected).getBytes());
|
||||
assertEquals(log, expected);
|
||||
}
|
||||
|
||||
public void shouldSerializeAndDeserializeWithNull_3() {
|
||||
String applicationType = null;
|
||||
SortedMap<String, String> dimensions = null;
|
||||
String message = "Hello, world!";
|
||||
Log expected = new Log(applicationType, dimensions, message);
|
||||
|
||||
Log log = Logs.fromJson(Logs.toJson(expected).getBytes());
|
||||
assertEquals(log, expected);
|
||||
}
|
||||
|
||||
public void shouldSerializeAndDeserializeWithEmpty() {
|
||||
String applicationType = "";
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "WebService01");
|
||||
dimensions.put("environment", "production");
|
||||
dimensions.put("instance_id", "123");
|
||||
String message = "Hello, world!";
|
||||
Log expected = new Log(applicationType, dimensions, message);
|
||||
|
||||
Log log = Logs.fromJson(Logs.toJson(expected).getBytes());
|
||||
assertEquals(log, expected);
|
||||
}
|
||||
|
||||
public void shouldSerializeAndDeserializeWithEmpty_2() {
|
||||
String applicationType = "apache";
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "WebService01");
|
||||
dimensions.put("environment", "production");
|
||||
dimensions.put("instance_id", "123");
|
||||
String message = "";
|
||||
Log expected = new Log(applicationType, dimensions, message);
|
||||
|
||||
Log log = Logs.fromJson(Logs.toJson(expected).getBytes());
|
||||
assertEquals(log, expected);
|
||||
}
|
||||
|
||||
public void shouldSerializeAndDeserializeWithEmpty_3() {
|
||||
String applicationType = "";
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "WebService01");
|
||||
dimensions.put("environment", "production");
|
||||
dimensions.put("instance_id", "123");
|
||||
String message = "";
|
||||
Log expected = new Log(applicationType, dimensions, message);
|
||||
|
||||
Log log = Logs.fromJson(Logs.toJson(expected).getBytes());
|
||||
assertEquals(log, expected);
|
||||
}
|
||||
|
||||
public void shouldSerializeValueUTF() {
|
||||
String applicationType = "foôbár";
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "foôbár");
|
||||
dimensions.put("instance_id", "123");
|
||||
String message = "boôbár";
|
||||
Log log = new Log(applicationType, dimensions, message);
|
||||
String json = Logs.toJson(log);
|
||||
assertEquals(
|
||||
json,
|
||||
"{\"application_type\":\"foôbár\",\"dimensions\":{\"app_name\":\"foôbár\",\"instance_id\":\"123\"}," +
|
||||
"\"message\":\"boôbár\"}");
|
||||
}
|
||||
|
||||
public void shouldSerializeAndDeserializeUTF8() throws UnsupportedEncodingException {
|
||||
String applicationType = "foôbár";
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "foôbár");
|
||||
dimensions.put("environment", "production");
|
||||
dimensions.put("instance_id", "123");
|
||||
String message = "foôbár";
|
||||
Log expected = new Log(applicationType, dimensions, message);
|
||||
|
||||
Log log = Logs.fromJson(Logs.toJson(expected).getBytes("UTF-8"));
|
||||
assertEquals(log, expected);
|
||||
}
|
||||
|
||||
public void shouldSerializeAndDeserializeUTF8_2() throws UnsupportedEncodingException {
|
||||
String applicationType = "fo\u00f4b\u00e1r";
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "fo\u00f4b\u00e1r");
|
||||
dimensions.put("environment", "production");
|
||||
dimensions.put("instance_id", "123");
|
||||
String message = "fo\u00f4b\u00e1r";
|
||||
Log expected = new Log(applicationType, dimensions, message);
|
||||
|
||||
Log log = Logs.fromJson(Logs.toJson(expected).getBytes("UTF-8"));
|
||||
assertEquals(log, expected);
|
||||
}
|
||||
|
||||
public void shouldSerializeAndDeserializeUTF8_3() throws UnsupportedEncodingException {
|
||||
String applicationType = "fo\u00f4b\u00e1r";
|
||||
String applicationType2 = "foôbár";
|
||||
SortedMap<String, String> dimensions = new TreeMap<String, String>();
|
||||
dimensions.put("app_name", "fo\u00f4b\u00e1r");
|
||||
dimensions.put("environment", "production");
|
||||
dimensions.put("instance_id", "123");
|
||||
SortedMap<String, String> dimensions2 = new TreeMap<String, String>();
|
||||
dimensions2.put("app_name", "foôbár");
|
||||
dimensions2.put("environment", "production");
|
||||
dimensions2.put("instance_id", "123");
|
||||
String message = "fo\u00f4b\u00e1r";
|
||||
String message2 = "foôbár";
|
||||
Log expected_escaped = new Log(applicationType, dimensions, message);
|
||||
Log expected_nonescaped = new Log(applicationType2, dimensions2, message2);
|
||||
|
||||
Log log = Logs.fromJson(Logs.toJson(expected_escaped).getBytes("UTF-8"));
|
||||
assertEquals(log, expected_nonescaped);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package monasca.log.api.resource;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
|
||||
import monasca.common.dropwizard.AbstractResourceTest;
|
||||
import monasca.log.api.resource.exception.ConstraintViolationExceptionMapper;
|
||||
import monasca.log.api.resource.exception.IllegalArgumentExceptionMapper;
|
||||
import monasca.log.api.resource.exception.JsonMappingExceptionManager;
|
||||
import monasca.log.api.resource.exception.JsonProcessingExceptionMapper;
|
||||
import monasca.log.api.resource.exception.ThrowableExceptionMapper;
|
||||
|
||||
|
||||
/**
|
||||
* Support class for monitoring resource tests.
|
||||
*/
|
||||
public abstract class AbstractMonApiResourceTest extends AbstractResourceTest {
|
||||
@Override
|
||||
protected void setupResources() throws Exception {
|
||||
addSingletons(new IllegalArgumentExceptionMapper(), new JsonProcessingExceptionMapper(), new JsonMappingExceptionManager(),
|
||||
new ConstraintViolationExceptionMapper(), new ThrowableExceptionMapper<Throwable>() {});
|
||||
|
||||
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
|
||||
objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,701 @@
|
|||
/*
|
||||
* Copyright 2015 Fujitsu Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package monasca.log.api.resource;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
|
||||
import monasca.log.api.app.LogService;
|
||||
import monasca.log.api.app.command.CreateLogCommand;
|
||||
import monasca.log.api.model.Log;
|
||||
import monasca.log.api.resource.exception.ErrorMessages;
|
||||
|
||||
@Test
|
||||
public class LogResourceTest extends AbstractMonApiResourceTest {
|
||||
private String applicationType;
|
||||
private String dimensionsStr;
|
||||
private Map<String, String> dimensionsMap;
|
||||
private String jsonMessage;
|
||||
private String textMessage;
|
||||
private String tenantId;
|
||||
private String longString;
|
||||
private LogService service;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void setupResources() throws Exception {
|
||||
super.setupResources();
|
||||
applicationType = "apache";
|
||||
dimensionsStr = "app_name:WebService01,environment:production";
|
||||
dimensionsMap = new HashMap<String, String>();
|
||||
dimensionsMap.put("app_name", "WebService01");
|
||||
dimensionsMap.put("environment", "production");
|
||||
jsonMessage = "{\n \"message\":\"Hello, world!\",\n \"from\":\"hoover\"\n}";
|
||||
textMessage = "Hello, world!";
|
||||
tenantId = "abc";
|
||||
longString =
|
||||
"12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890"
|
||||
+ "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890"
|
||||
+ "12345678901234567890123456789012345678901234567890123456";
|
||||
|
||||
service = mock(LogService.class);
|
||||
doNothing().when(service).create(any(Log.class), anyString());
|
||||
|
||||
addResources(new LogResource(service));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessage() {
|
||||
ClientResponse response = createResponseForJson(null, null, jsonMessage);
|
||||
Log log = new Log(null, null, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessageByText() {
|
||||
ClientResponse response = createResponseForText(null, null, jsonMessage);
|
||||
Log log = new Log(null, null, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessage() {
|
||||
ClientResponse response = createResponseForText(null, null, textMessage);
|
||||
Log log = new Log(null, null, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessageByJson() {
|
||||
ClientResponse response = createResponseForJson(null, null, textMessage);
|
||||
Log log = new Log(null, null, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateEmptyMessageByJson() {
|
||||
ClientResponse response = createResponseForJson(null, null, "");
|
||||
Log log = new Log(null, null, "");
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateEmptyMessageByText() {
|
||||
ClientResponse response = createResponseForText(null, null, "");
|
||||
Log log = new Log(null, null, "");
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessageWithType() {
|
||||
ClientResponse response = createResponseForJson(applicationType, null, jsonMessage);
|
||||
Log log = new Log(applicationType, null, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessageWithType() {
|
||||
ClientResponse response = createResponseForText(applicationType, null, textMessage);
|
||||
Log log = new Log(applicationType, null, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessageWithEmptyType() {
|
||||
ClientResponse response = createResponseForJson("", null, jsonMessage);
|
||||
Log log = new Log("", null, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessageWithEmptyType() {
|
||||
ClientResponse response = createResponseForText("", null, textMessage);
|
||||
Log log = new Log("", null, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessageWithNonNumericAZType() {
|
||||
ClientResponse response = createResponseForJson("azAZ19.-_", null, jsonMessage);
|
||||
Log log = new Log("azAZ19.-_", null, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessageWithNonNumericAZType() {
|
||||
ClientResponse response = createResponseForText("azAZ19.-_", null, textMessage);
|
||||
Log log = new Log("azAZ19.-_", null, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessageWithSpaceType() {
|
||||
ClientResponse response = createResponseForJson(" apache ", null, jsonMessage);
|
||||
Log log = new Log("apache", null, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessageWithSpaceType() {
|
||||
ClientResponse response = createResponseForText(" apache ", null, textMessage);
|
||||
Log log = new Log("apache", null, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithIllegalCharsInType() {
|
||||
ClientResponse response = createResponseForJson("@apache", null, jsonMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"Application type @apache may only contain: a-z A-Z 0-9 _ - .");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithIllegalCharsInType() {
|
||||
ClientResponse response = createResponseForText("@apache", null, textMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"Application type @apache may only contain: a-z A-Z 0-9 _ - .");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithTooLongType() {
|
||||
String tooLongType = longString;
|
||||
ClientResponse response = createResponseForJson(tooLongType, null, jsonMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
String.format("Application type %s must be %d characters or less", tooLongType, CreateLogCommand.MAX_NAME_LENGTH));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithTooLongType() {
|
||||
String tooLongType = longString;
|
||||
ClientResponse response = createResponseForText(tooLongType, null, textMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
String.format("Application type %s must be %d characters or less", tooLongType, CreateLogCommand.MAX_NAME_LENGTH));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessageWithDimensions() {
|
||||
ClientResponse response = createResponseForJson(null, dimensionsStr, jsonMessage);
|
||||
Log log = new Log(null, dimensionsMap, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessageWithDimensions() {
|
||||
ClientResponse response = createResponseForText(null, dimensionsStr, textMessage);
|
||||
Log log = new Log(null, dimensionsMap, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessageWithEmptyDimensions() {
|
||||
ClientResponse response = createResponseForJson(null, "", jsonMessage);
|
||||
Log log = new Log(null, null, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessageWithEmptyDimensions() {
|
||||
ClientResponse response = createResponseForText(null, "", textMessage);
|
||||
Log log = new Log(null, null, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithPartiallyEmptyDimensions() {
|
||||
String partiallyEmptyDimensions = ",environment:production";
|
||||
Map<String, String> expectedDimensionsMap = new HashMap<String, String>();
|
||||
expectedDimensionsMap.put("environment", "production");
|
||||
|
||||
ClientResponse response = createResponseForJson(null, partiallyEmptyDimensions, jsonMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "Dimension cannot be empty");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithPartiallyEmptyDimensions() {
|
||||
String partiallyEmptyDimensions = ",environment:production";
|
||||
Map<String, String> expectedDimensionsMap = new HashMap<String, String>();
|
||||
expectedDimensionsMap.put("environment", "production");
|
||||
|
||||
ClientResponse response = createResponseForText(null, partiallyEmptyDimensions, textMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "Dimension cannot be empty");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithPartiallyEmptyDimensions_2() {
|
||||
String partiallyEmptyDimensions = "environment:production,";
|
||||
Map<String, String> expectedDimensionsMap = new HashMap<String, String>();
|
||||
expectedDimensionsMap.put("environment", "production");
|
||||
|
||||
ClientResponse response = createResponseForJson(null, partiallyEmptyDimensions, jsonMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "Dimension cannot be empty");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithPartiallyEmptyDimensions_2() {
|
||||
String partiallyEmptyDimensions = "environment:production,";
|
||||
Map<String, String> expectedDimensionsMap = new HashMap<String, String>();
|
||||
expectedDimensionsMap.put("environment", "production");
|
||||
|
||||
ClientResponse response = createResponseForText(null, partiallyEmptyDimensions, textMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "Dimension cannot be empty");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessageWithDimensionsWithSpaceInKey() {
|
||||
String spaceInKeyDimensions = " app_name :WebService01, environment :production";
|
||||
|
||||
ClientResponse response = createResponseForJson(null, spaceInKeyDimensions, jsonMessage);
|
||||
Log log = new Log(null, dimensionsMap, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessageWithDimensionsWithSpaceInKey() {
|
||||
String spaceInKeyDimensions = " app_name :WebService01, environment :production";
|
||||
|
||||
ClientResponse response = createResponseForText(null, spaceInKeyDimensions, textMessage);
|
||||
Log log = new Log(null, dimensionsMap, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessageWithDimensionsWithSpaceInValue() {
|
||||
String spaceInValueDimensions = "app_name: WebService01 ,environment: production ";
|
||||
|
||||
ClientResponse response = createResponseForJson(null, spaceInValueDimensions, jsonMessage);
|
||||
Log log = new Log(null, dimensionsMap, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessageWithDimensionsWithSpaceInValue() {
|
||||
String spaceInValueDimensions = "app_name: WebService01 ,environment: production ";
|
||||
|
||||
ClientResponse response = createResponseForText(null, spaceInValueDimensions, textMessage);
|
||||
Log log = new Log(null, dimensionsMap, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithEmptyKeyDimensions() {
|
||||
String emptyKeyDimensionsStr = ":WebService01,environment:production";
|
||||
ClientResponse response = createResponseForJson(null, emptyKeyDimensionsStr, jsonMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "Dimension name cannot be empty");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithEmptyKeyDimensions() {
|
||||
String emptyKeyDimensionsStr = ":WebService01,environment:production";
|
||||
ClientResponse response = createResponseForText(null, emptyKeyDimensionsStr, textMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "Dimension name cannot be empty");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithEmptyValueDimensions() {
|
||||
String emptyKeyDimensionsStr = "app_name:,environment:production";
|
||||
ClientResponse response = createResponseForJson(null, emptyKeyDimensionsStr, jsonMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "Dimension app_name cannot have an empty value");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithEmptyValueDimensions() {
|
||||
String emptyKeyDimensionsStr = "app_name:,environment:production";
|
||||
ClientResponse response = createResponseForText(null, emptyKeyDimensionsStr, textMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "Dimension app_name cannot have an empty value");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithDimensionsWithoutColon() {
|
||||
String emptyKeyDimensionsStr = "app_name,environment:production";
|
||||
ClientResponse response = createResponseForJson(null, emptyKeyDimensionsStr, jsonMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "app_name is not a valid dimension");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithDimensionsWithoutColon() {
|
||||
String emptyKeyDimensionsStr = "app_name,environment:production";
|
||||
ClientResponse response = createResponseForText(null, emptyKeyDimensionsStr, textMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "app_name is not a valid dimension");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessageWithMultipleColonDimensions() {
|
||||
String multipleColonDimensionsStr = "app_name:WebService01:abc,environment:production";
|
||||
Map<String, String> multipleColonDimensionsMap = new HashMap<String, String>();
|
||||
multipleColonDimensionsMap.put("app_name", "WebService01:abc");
|
||||
multipleColonDimensionsMap.put("environment", "production");
|
||||
ClientResponse response = createResponseForJson(null, multipleColonDimensionsStr, jsonMessage);
|
||||
Log log = new Log(null, multipleColonDimensionsMap, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessageWithMultipleColonDimensions() {
|
||||
String multipleColonDimensionsStr = "app_name:WebService01:abc,environment:production";
|
||||
Map<String, String> multipleColonDimensionsMap = new HashMap<String, String>();
|
||||
multipleColonDimensionsMap.put("app_name", "WebService01:abc");
|
||||
multipleColonDimensionsMap.put("environment", "production");
|
||||
ClientResponse response = createResponseForText(null, multipleColonDimensionsStr, textMessage);
|
||||
Log log = new Log(null, multipleColonDimensionsMap, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessageWithDimensionsWithoutIllgalCharsInKey() {
|
||||
String legalCharsInKeyDimensionsStr = "azAZ19.-_:WebService01,environment:production";
|
||||
Map<String, String> expectedDimensionsMap = new HashMap<String, String>();
|
||||
expectedDimensionsMap.put("azAZ19.-_", "WebService01");
|
||||
expectedDimensionsMap.put("environment", "production");
|
||||
|
||||
ClientResponse response = createResponseForJson(null, legalCharsInKeyDimensionsStr, jsonMessage);
|
||||
Log log = new Log(null, expectedDimensionsMap, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessageWithDimensionsWithoutIllegalCharsInKey() {
|
||||
String legalCharsInKeyDimensionsStr = "azAZ19.-_:WebService01,environment:production";
|
||||
Map<String, String> expectedDimensionsMap = new HashMap<String, String>();
|
||||
expectedDimensionsMap.put("azAZ19.-_", "WebService01");
|
||||
expectedDimensionsMap.put("environment", "production");
|
||||
|
||||
ClientResponse response = createResponseForText(null, legalCharsInKeyDimensionsStr, textMessage);
|
||||
Log log = new Log(null, expectedDimensionsMap, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithIllegalCharsInKey() {
|
||||
String illegalCharsInKeyDimensionsStr = "{app_name:WebService01,environment:production";
|
||||
ClientResponse response = createResponseForJson(null, illegalCharsInKeyDimensionsStr, jsonMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"Dimension name {app_name may not contain: > < = { } ( ) ' \" , ; &");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithIllegalCharsInKey() {
|
||||
String illegalCharsInKeyDimensionsStr = "{app_name:WebService01,environment:production";
|
||||
ClientResponse response = createResponseForText(null, illegalCharsInKeyDimensionsStr, textMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"Dimension name {app_name may not contain: > < = { } ( ) ' \" , ; &");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithIllegalCharsInKey_2() {
|
||||
String illegalCharsInKeyDimensionsStr = "app=name:WebService01,environment:production";
|
||||
ClientResponse response = createResponseForJson(null, illegalCharsInKeyDimensionsStr, jsonMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"Dimension name app=name may not contain: > < = { } ( ) ' \" , ; &");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithIllegalCharsInKey_2() {
|
||||
String illegalCharsInKeyDimensionsStr = "app=name:WebService01,environment:production";
|
||||
ClientResponse response = createResponseForText(null, illegalCharsInKeyDimensionsStr, textMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"Dimension name app=name may not contain: > < = { } ( ) ' \" , ; &");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithLegalCharsInValue() {
|
||||
String legalCharsInValueDimensionsStr = "app_name:@WebService01,environment:production";
|
||||
Map<String, String> legalCharsInValueDimensionsMap = new HashMap<String, String>();
|
||||
legalCharsInValueDimensionsMap.put("app_name", "@WebService01");
|
||||
legalCharsInValueDimensionsMap.put("environment", "production");
|
||||
ClientResponse response = createResponseForJson(null, legalCharsInValueDimensionsStr, jsonMessage);
|
||||
Log log = new Log(null, legalCharsInValueDimensionsMap, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithLegalCharsInValue() {
|
||||
String legalCharsInValueDimensionsStr = "app_name:@WebService01,environment:production";
|
||||
Map<String, String> legalCharsInValueDimensionsMap = new HashMap<String, String>();
|
||||
legalCharsInValueDimensionsMap.put("app_name", "@WebService01");
|
||||
legalCharsInValueDimensionsMap.put("environment", "production");
|
||||
ClientResponse response = createResponseForText(null, legalCharsInValueDimensionsStr, textMessage);
|
||||
Log log = new Log(null, legalCharsInValueDimensionsMap, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithLegalCharsInValue_2() {
|
||||
String legalCharsInValueDimensionsStr = "app_name:Web Service01,environment:production";
|
||||
Map<String, String> legalCharsInValueDimensionsMap = new HashMap<String, String>();
|
||||
legalCharsInValueDimensionsMap.put("app_name", "Web Service01");
|
||||
legalCharsInValueDimensionsMap.put("environment", "production");
|
||||
ClientResponse response = createResponseForJson(null, legalCharsInValueDimensionsStr, jsonMessage);
|
||||
Log log = new Log(null, legalCharsInValueDimensionsMap, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithLegalCharsInValue_2() {
|
||||
String legalCharsInValueDimensionsStr = "app_name:Web Service01,environment:production";
|
||||
Map<String, String> legalCharsInValueDimensionsMap = new HashMap<String, String>();
|
||||
legalCharsInValueDimensionsMap.put("app_name", "Web Service01");
|
||||
legalCharsInValueDimensionsMap.put("environment", "production");
|
||||
ClientResponse response = createResponseForText(null, legalCharsInValueDimensionsStr, textMessage);
|
||||
Log log = new Log(null, legalCharsInValueDimensionsMap, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithTooLongKey() {
|
||||
String tooLongKey = longString;
|
||||
ClientResponse response = createResponseForJson(null, tooLongKey + ":abc", jsonMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
String.format("Dimension name %s must be 255 characters or less", tooLongKey));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithTooLongKey() {
|
||||
String tooLongKey = longString;
|
||||
ClientResponse response = createResponseForText(null, tooLongKey + ":abc", textMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
String.format("Dimension name %s must be 255 characters or less", tooLongKey));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateJsonMessageWithTooLongValue() {
|
||||
String tooLongValue = longString;
|
||||
ClientResponse response = createResponseForJson(null, "abc:" + tooLongValue, jsonMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
String.format("Dimension value %s must be 255 characters or less", tooLongValue));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTextMessageWithTooLongValue() {
|
||||
String tooLongValue = longString;
|
||||
ClientResponse response = createResponseForText(null, "abc:" + tooLongValue, textMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
String.format("Dimension value %s must be 255 characters or less", tooLongValue));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateJsonMessageWithTypeAndDimensions() {
|
||||
ClientResponse response = createResponseForJson(applicationType, dimensionsStr, jsonMessage);
|
||||
Log log = new Log(applicationType, dimensionsMap, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateTextMessageWithTypeAndDimensions() {
|
||||
ClientResponse response = createResponseForText(applicationType, dimensionsStr, textMessage);
|
||||
Log log = new Log(applicationType, dimensionsMap, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq(tenantId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTooLongJsonMessage() {
|
||||
String tmpString = longString + longString + longString + longString;
|
||||
StringBuffer buf = new StringBuffer(1024 * 1024 + 1);
|
||||
buf.append("{\"a\":\"");
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
buf.append(tmpString);
|
||||
}
|
||||
buf.append("\"}");
|
||||
String tooLongMessage = buf.toString();
|
||||
ClientResponse response = createResponseForJson(null, null, tooLongMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "Log must be 1048576 characters or less");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldErrorOnCreateTooLongTextMessage() {
|
||||
String tmpString = longString + longString + longString + longString;
|
||||
StringBuffer buf = new StringBuffer(1024 * 1024 + 1);
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
buf.append(tmpString);
|
||||
}
|
||||
buf.append('0');
|
||||
String tooLongMessage = buf.toString();
|
||||
ClientResponse response = createResponseForText(null, null, tooLongMessage);
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "Log must be 1048576 characters or less");
|
||||
}
|
||||
|
||||
public void shouldErrorOnCreateJsonMessageWithCrossTenant() {
|
||||
ClientResponse response = createResponseForJsonWithCrossTenant(null, null, jsonMessage, "illegal-role", "def");
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("forbidden", 403, "Project abc cannot POST cross tenant");
|
||||
}
|
||||
|
||||
public void shouldErrorOnCreateTextMessageWithCrossTenant() {
|
||||
ClientResponse response = createResponseForTextWithCrossTenant(null, null, textMessage, "illegal-role", "def");
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("forbidden", 403, "Project abc cannot POST cross tenant");
|
||||
}
|
||||
|
||||
public void shouldCreateJsonMessageWithCrossTenant() {
|
||||
ClientResponse response = createResponseForJsonWithCrossTenant(null, null, jsonMessage, "monitoring-delegate", "def");
|
||||
Log log = new Log(null, null, jsonMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq("def"));
|
||||
}
|
||||
|
||||
public void shouldCreateTextMessageWithCrossTenant() {
|
||||
ClientResponse response = createResponseForTextWithCrossTenant(null, null, textMessage, "monitoring-delegate", "def");
|
||||
Log log = new Log(null, null, textMessage);
|
||||
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).create(eq(log), eq("def"));
|
||||
}
|
||||
|
||||
private ClientResponse createResponseForJson(String applicationType, String dimensions, String message) {
|
||||
WebResource.Builder builder =
|
||||
client().resource("/v2.0/log/single").header("X-Tenant-Id", tenantId).header("Content-Type", MediaType.APPLICATION_JSON);
|
||||
if (applicationType != null)
|
||||
builder = builder.header("X-Application-Type", applicationType);
|
||||
if (dimensions != null)
|
||||
builder = builder.header("X-Dimensions", dimensions);
|
||||
return builder.post(ClientResponse.class, message);
|
||||
}
|
||||
|
||||
private ClientResponse createResponseForText(String applicationType, String dimensions, String message) {
|
||||
WebResource.Builder builder = client().resource("/v2.0/log/single").header("X-Tenant-Id", tenantId).header("Content-Type", MediaType.TEXT_PLAIN);
|
||||
if (applicationType != null)
|
||||
builder = builder.header("X-Application-Type", applicationType);
|
||||
if (dimensions != null)
|
||||
builder = builder.header("X-Dimensions", dimensions);
|
||||
return builder.post(ClientResponse.class, message);
|
||||
}
|
||||
|
||||
private ClientResponse createResponseForJsonWithCrossTenant(String applicationType, String dimensions, String message, String roles,
|
||||
String crossTenantId) {
|
||||
WebResource.Builder builder =
|
||||
client().resource("/v2.0/log/single?tenant_id=" + crossTenantId).header("X-Tenant-Id", tenantId).header("X-Roles", roles)
|
||||
.header("Content-Type", MediaType.APPLICATION_JSON);
|
||||
if (applicationType != null)
|
||||
builder = builder.header("X-Application-Type", applicationType);
|
||||
if (dimensions != null)
|
||||
builder = builder.header("X-Dimensions", dimensions);
|
||||
return builder.post(ClientResponse.class, message);
|
||||
}
|
||||
|
||||
private ClientResponse createResponseForTextWithCrossTenant(String applicationType, String dimensions, String message, String roles,
|
||||
String crossTenantId) {
|
||||
WebResource.Builder builder =
|
||||
client().resource("/v2.0/log/single?tenant_id=" + crossTenantId).header("X-Tenant-Id", tenantId).header("X-Roles", roles)
|
||||
.header("Content-Type", MediaType.TEXT_PLAIN);
|
||||
if (applicationType != null)
|
||||
builder = builder.header("X-Application-Type", applicationType);
|
||||
if (dimensions != null)
|
||||
builder = builder.header("X-Dimensions", dimensions);
|
||||
return builder.post(ClientResponse.class, message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package monasca.log.api.resource.exception;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import monasca.log.api.resource.exception.ErrorMessage;
|
||||
|
||||
/**
|
||||
* Error message utilities.
|
||||
*/
|
||||
public final class ErrorMessages {
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
public interface ErrorMessageMatcher {
|
||||
void matches(String faultType, int code, String messagePrefix);
|
||||
|
||||
void matches(String faultType, int code, String messagePrefix, @Nullable String detailsPrefix);
|
||||
}
|
||||
|
||||
public static ErrorMessageMatcher assertThat(final String errorMessage) {
|
||||
try {
|
||||
JsonNode node = MAPPER.readTree(errorMessage);
|
||||
final String rootKey = node.fieldNames().next();
|
||||
node = node.get(rootKey);
|
||||
final ErrorMessage message = MAPPER.reader(ErrorMessage.class).readValue(node);
|
||||
|
||||
return new ErrorMessageMatcher() {
|
||||
@Override
|
||||
public void matches(String faultType, int code, String messagePrefix) {
|
||||
matches(faultType, code, messagePrefix, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void matches(String faultType, int code, String messagePrefix,
|
||||
@Nullable String detailsPrefix) {
|
||||
assertEquals(rootKey, faultType);
|
||||
assertEquals(message.code, code);
|
||||
assertTrue(message.message.startsWith(messagePrefix), message.message);
|
||||
if (detailsPrefix != null)
|
||||
assertTrue(message.details.startsWith(detailsPrefix), message.details);
|
||||
}
|
||||
};
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<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>monasca</groupId>
|
||||
<artifactId>monasca-log-api-base</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<url>http://github.com/stackforge/monasca-log-api</url>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<!-- Versioning -->
|
||||
<exec.args>${version} ${sun.java.command}</exec.args>
|
||||
<skipITs>true</skipITs>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
</properties>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git@github.com:stackforge/monasca-log-api</connection>
|
||||
<developerConnection>scm:git:git@github.com:stackforge/monasca-log-api</developerConnection>
|
||||
</scm>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>package-execution</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>clean-execution</id>
|
||||
<phase>clean</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<executable>run_maven.sh</executable>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,49 @@
|
|||
#!/bin/bash
|
||||
# Download maven 3 if the system maven isn't maven 3
|
||||
VERSION=`mvn -v | grep "Apache Maven 3"`
|
||||
if [ -z "${VERSION}" ]; then
|
||||
curl http://archive.apache.org/dist/maven/binaries/apache-maven-3.2.1-bin.tar.gz > apache-maven-3.2.1-bin.tar.gz
|
||||
tar -xvzf apache-maven-3.2.1-bin.tar.gz
|
||||
MVN=${PWD}/apache-maven-3.2.1/bin/mvn
|
||||
else
|
||||
MVN=mvn
|
||||
fi
|
||||
|
||||
# Get the expected common version
|
||||
COMMON_VERSION=$1
|
||||
# Get rid of the version argument
|
||||
shift
|
||||
|
||||
# Get rid of the java property name containing the args
|
||||
shift
|
||||
|
||||
RUN_BUILD=false
|
||||
for ARG in $*; do
|
||||
if [ "$ARG" = "package" ]; then
|
||||
RUN_BUILD=true
|
||||
fi
|
||||
if [ "$ARG" = "install" ]; then
|
||||
RUN_BUILD=true
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $RUN_BUILD = "true" ]; then
|
||||
( cd common; ./build_common.sh ${MVN} ${COMMON_VERSION} )
|
||||
RC=$?
|
||||
if [ $RC != 0 ]; then
|
||||
exit $RC
|
||||
fi
|
||||
fi
|
||||
|
||||
# Invoke the maven 3 on the real pom.xml
|
||||
( cd java; ${MVN} -DgitRevision=`git rev-list HEAD --max-count 1 --abbrev=0 --abbrev-commit` $* )
|
||||
|
||||
RC=$?
|
||||
|
||||
# Copy the jars where the publisher will find them
|
||||
if [ $RUN_BUILD = "true" ]; then
|
||||
ln -sf java/target target
|
||||
fi
|
||||
|
||||
rm -fr apache-maven-3.2.1*
|
||||
exit $RC
|
Loading…
Reference in New Issue