forked from hugegraph/hugegraph-sync
Merge pull request #2628 from apache/intro-commons-v2
**TODO:** After this PR merged, change the README in commons & pin a [issue](https://github.com/hugegraph/hugegraph-hubble/issues/371) to notify all users/devs to know the context (also update the repo description...) Finally, we may need start a discussion for marking commons repo in achieved status
This commit is contained in:
commit
0a0f2c3bfa
|
@ -42,7 +42,8 @@ github:
|
|||
- Analyze (java)
|
||||
- CodeQL
|
||||
- check-license
|
||||
- build (memory, 11)
|
||||
- build-server (memory, 11)
|
||||
- build-commons (11)
|
||||
required_pull_request_reviews:
|
||||
dismiss_stale_reviews: true
|
||||
require_code_owner_reviews: false
|
||||
|
|
|
@ -12,4 +12,5 @@ hugegraph-store/hg-store-dist/src/assembly/static/bin/libjemalloc_aarch64.so exp
|
|||
.github/ export-ignore
|
||||
.idea/ export-ignore
|
||||
install-dist/scripts/ export-ignore
|
||||
hugegraph-commons/hugegraph-dist/ export-ignore
|
||||
docker/ export-ignore
|
||||
|
|
|
@ -32,7 +32,7 @@ jobs:
|
|||
|
||||
- name: mvn install
|
||||
run: |
|
||||
mvn install -DskipTests=true -ntp
|
||||
mvn install -Dmaven.test.skip=true -ntp
|
||||
- name: generate current dependencies
|
||||
run: |
|
||||
bash $SCRIPT_DEPENDENCY/regenerate_known_dependencies.sh current-dependencies.txt
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
name: "HugeGraph-Commons CI"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- /^release-.*$/
|
||||
- /^test-.*$/
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build-commons:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# TODO: reset use stage to false later
|
||||
USE_STAGE: 'true' # Whether to include the stage repository.
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
JAVA_VERSION: ['11']
|
||||
|
||||
steps:
|
||||
- name: Install JDK ${{ matrix.JAVA_VERSION }}
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: ${{ matrix.JAVA_VERSION }}
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Use staged maven repo settings
|
||||
if: ${{ env.USE_STAGE == 'true' }}
|
||||
run: |
|
||||
cp $HOME/.m2/settings.xml /tmp/settings.xml
|
||||
cp -vf .github/configs/settings.xml $HOME/.m2/settings.xml && cat $HOME/.m2/settings.xml
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
mvn compile -Dmaven.javadoc.skip=true -ntp
|
||||
|
||||
- name: Run common test
|
||||
run: |
|
||||
mvn test -pl hugegraph-commons/hugegraph-common -Dtest=UnitTestSuite
|
||||
|
||||
- name: Run rpc test
|
||||
run: |
|
||||
mvn test -pl hugegraph-commons/hugegraph-rpc -Dtest=UnitTestSuite
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3.0.0
|
||||
with:
|
||||
file: target/jacoco.xml
|
|
@ -1,4 +1,4 @@
|
|||
name: "Graph PD & Store & Hstore CI"
|
||||
name: "HugeGraph-PD & Store & Hstore CI"
|
||||
|
||||
on:
|
||||
push:
|
||||
|
@ -14,7 +14,8 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# TODO: avoid duplicated env setup in pd & store
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
# TODO: reset use stage to false later
|
||||
USE_STAGE: 'true' # Whether to include the stage repository.
|
||||
# TODO: remove outdated env
|
||||
TRAVIS_DIR: hugegraph-server/hugegraph-dist/src/assembly/travis
|
||||
REPORT_DIR: target/site/jacoco
|
||||
|
@ -46,11 +47,11 @@ jobs:
|
|||
|
||||
- name: Run common test
|
||||
run: |
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am -P pd-common-test
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am -P pd-common-test -DskipCommonsTests=true
|
||||
|
||||
- name: Run core test
|
||||
run: |
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am -P pd-core-test
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am -P pd-core-test -DskipCommonsTests=true
|
||||
|
||||
# The above tests do not require starting a PD instance.
|
||||
|
||||
|
@ -64,11 +65,11 @@ jobs:
|
|||
|
||||
- name: Run client test
|
||||
run: |
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am -P pd-client-test
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am -P pd-client-test -DskipCommonsTests=true
|
||||
|
||||
- name: Run rest test
|
||||
run: |
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am -P pd-rest-test
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am -P pd-rest-test -DskipCommonsTests=true
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3.0.0
|
||||
|
@ -79,7 +80,7 @@ jobs:
|
|||
# TODO: avoid duplicated env setup
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
USE_STAGE: 'true' # Whether to include the stage repository.
|
||||
# TODO: remove outdated env
|
||||
TRAVIS_DIR: hugegraph-server/hugegraph-dist/src/assembly/travis
|
||||
REPORT_DIR: target/site/jacoco
|
||||
|
@ -120,27 +121,27 @@ jobs:
|
|||
|
||||
- name: Run common test
|
||||
run: |
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-common-test
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-common-test -DskipCommonsTests=true
|
||||
|
||||
- name: Run client test
|
||||
run: |
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-client-test
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-client-test -DskipCommonsTests=true
|
||||
|
||||
- name: Run core test
|
||||
run: |
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-core-test
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-core-test -DskipCommonsTests=true
|
||||
|
||||
- name: Run rocksdb test
|
||||
run: |
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-rocksdb-test
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-rocksdb-test -DskipCommonsTests=true
|
||||
|
||||
- name: Run server test
|
||||
run: |
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-server-test
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-server-test -DskipCommonsTests=true
|
||||
|
||||
- name: Run raft-core test
|
||||
run: |
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-raftcore-test
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-raftcore-test -DskipCommonsTests=true
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3.0.0
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name: "Graph Server CI"
|
||||
name: "HugeGraph-Server CI"
|
||||
|
||||
on:
|
||||
push:
|
||||
|
@ -9,11 +9,13 @@ on:
|
|||
pull_request:
|
||||
|
||||
jobs:
|
||||
# TODO: rename to build-server later
|
||||
build:
|
||||
# TODO: we need test & replace it to ubuntu-24.04 or ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
# TODO: reset use stage to false later
|
||||
USE_STAGE: 'true' # Whether to include the stage repository.
|
||||
TRAVIS_DIR: hugegraph-server/hugegraph-dist/src/assembly/travis
|
||||
REPORT_DIR: target/site/jacoco
|
||||
BACKEND: ${{ matrix.BACKEND }}
|
||||
|
|
|
@ -6,7 +6,7 @@ Required:
|
|||
* Java 11
|
||||
* Maven 3.5+
|
||||
|
||||
To build without executing tests: `mvn clean package -DskipTests`
|
||||
To build without executing tests: `mvn clean package -Dmaven.test.skip=true`
|
||||
|
||||
## Building in IDEA
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
# hugegraph-commons
|
||||
|
||||
[![License](https://img.shields.io/badge/license-Apache%202-0E78BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[![codecov](https://codecov.io/gh/hugegraph/hugegraph-common/branch/master/graph/badge.svg)](https://codecov.io/gh/hugegraph/hugegraph-common)
|
||||
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.hugegraph/hugegraph-common/badge.svg)](https://mvnrepository.com/artifact/org.apache.hugegraph/hugegraph-common)
|
||||
[![CodeQL](https://github.com/apache/incubator-hugegraph-commons/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/apache/incubator-hugegraph-commons/actions/workflows/codeql-analysis.yml)
|
||||
[![hugegraph-commons ci](https://github.com/apache/incubator-hugegraph-commons/actions/workflows/ci.yml/badge.svg)](https://github.com/apache/incubator-hugegraph-commons/actions/workflows/ci.yml)
|
||||
|
||||
|
||||
hugegraph-commons is a common module for [HugeGraph](https://github.com/apache/hugegraph) and its peripheral components.
|
||||
hugegraph-commons encapsulates locks, configurations, events, iterators, rest and some
|
||||
numeric or collection util classes to simplify the development of HugeGraph and its components.
|
||||
|
||||
## Components
|
||||
|
||||
- Lock: atomic lock, key lock, lock group and lock manger
|
||||
- Config: register and load config option with security check
|
||||
- Event: listening and notification, do something asynchronously
|
||||
- Iterator: some iterators with extra functions, map, filter, extend, etc.
|
||||
- Rest: RESTful client implemented on OkHttp, POST, PUT, GET and DELETE
|
||||
- Util: performance analyzer, version checker, numeric and Collection utils, log and exception utils, etc.
|
||||
- Rpc: rpc component for inner module communication, currently it's based on [Sofa-RPC](https://github.com/sofastack/sofa-rpc)
|
||||
|
||||
You could use import the dependencies in `maven` like this:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.apache.hugegraph</groupId>
|
||||
<artifactId>hugegraph-common</artifactId>
|
||||
<version>1.2.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Learn More
|
||||
|
||||
The [doc page](https://hugegraph.apache.org/docs/) contains more information about hugegraph modules.
|
||||
|
||||
And here are links of other repositories:
|
||||
1. [hugegraph-server](https://github.com/apache/hugegraph) (graph's core component - OLTP server)
|
||||
2. [hugegraph-toolchain](https://github.com/apache/hugegraph-toolchain) (include loader/dashboard/tool/client)
|
||||
3. [hugegraph-computer](https://github.com/apache/hugegraph-computer) (graph processing system - OLAP)
|
||||
4. [hugegraph-website/doc](https://github.com/apache/hugegraph-doc) (include doc & website code)
|
||||
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
- Welcome to contribute to HugeGraph, please see [How to Contribute](https://hugegraph.apache.org/docs/contribution-guidelines/contribute/) for more information.
|
||||
- Note: It's recommended to use [GitHub Desktop](https://desktop.github.com/) to greatly simplify the PR and commit process.
|
||||
- Thank you to all the people who already contributed to HugeGraph!
|
||||
|
||||
[![contributors graph](https://contrib.rocks/image?repo=apache/hugegraph-commons)](https://github.com/apache/incubator-hugegraph-commons/graphs/contributors)
|
||||
|
||||
## Licence
|
||||
|
||||
Same as HugeGraph, hugegraph-commons are also licensed under [Apache 2.0](./LICENSE) License.
|
||||
|
||||
### Contact Us
|
||||
|
||||
---
|
||||
|
||||
- [GitHub Issues](https://github.com/apache/incubator-hugegraph-commons/issues): Feedback on usage issues and functional requirements (quick response)
|
||||
- Feedback Email: [dev@hugegraph.apache.org](mailto:dev@hugegraph.apache.org) ([subscriber](https://hugegraph.apache.org/docs/contribution-guidelines/subscribe/) only)
|
||||
- WeChat public account: Apache HugeGraph, welcome to scan this QR code to follow us.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/apache/incubator-hugegraph-doc/master/assets/images/wechat.png" alt="QR png" width="350"/>
|
|
@ -0,0 +1,23 @@
|
|||
# hugegraph-common
|
||||
|
||||
[![License](https://img.shields.io/badge/license-Apache%202-0E78BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[![Build Status](https://travis-ci.org/hugegraph/hugegraph-common.svg?branch=master)](https://travis-ci.org/hugegraph/hugegraph-common)
|
||||
[![codecov](https://codecov.io/gh/hugegraph/hugegraph-common/branch/master/graph/badge.svg)](https://codecov.io/gh/hugegraph/hugegraph-common)
|
||||
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.hugegraph/hugegraph-common/badge.svg)](https://mvnrepository.com/artifact/org.apache.hugegraph/hugegraph-common)
|
||||
|
||||
hugegraph-common is a common module for [HugeGraph](https://github.com/hugegraph/hugegraph) and its peripheral components.
|
||||
hugegraph-common encapsulates locks, configurations, events, iterators, rest and some
|
||||
numeric or collection util classes to simplify the development of HugeGraph and
|
||||
its components.
|
||||
|
||||
## Components
|
||||
|
||||
- Lock: atomic lock, key lock, lock group and lock manger
|
||||
- Config: register and load config option with security check
|
||||
- Event: listening and notification, do something asynchronously
|
||||
- Iterator: some iterators with extra functions, map, filter, extend, etc.
|
||||
- Rest: RESTful client implemented on Jersey, POST, PUT, GET and DELETE
|
||||
- Util: Performance analyzer, version checker, numeric and Collection utils, log and exception utils, etc.
|
||||
|
||||
## Licence
|
||||
The same as HugeGraph, hugegraph-common is also licensed under Apache 2.0 License.
|
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
export MAVEN_HOME=/home/scmtools/buildkit/maven/apache-maven-3.3.9/
|
||||
export JAVA_HOME=/home/scmtools/buildkit/java/jdk1.8.0_25/
|
||||
export PATH=$JAVA_HOME/bin:$MAVEN_HOME/bin:$PATH
|
||||
|
||||
mvn clean test -Dtest=UnitTestSuite
|
|
@ -0,0 +1,302 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
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.
|
||||
-->
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.hugegraph</groupId>
|
||||
<artifactId>hugegraph-commons</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>hugegraph-common</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<url>https://github.com/apache/incubator-hugegraph-commons/tree/master/hugegraph-common</url>
|
||||
<description>
|
||||
hugegraph-common is a common module for HugeGraph and its peripheral components.
|
||||
hugegraph-common encapsulates locks, configurations, events, iterators, rest and some
|
||||
numeric or collection util classes to simplify the development of HugeGraph and its
|
||||
components.
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<!-- Use parent params -->
|
||||
<lombok.version>1.18.8</lombok.version>
|
||||
<okhttp.version>4.10.0</okhttp.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Logging -->
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>${log4j2.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>${log4j2.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<version>${log4j2.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Utility -->
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>javax.json</artifactId>
|
||||
<version>${javax.json.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-configuration</groupId>
|
||||
<artifactId>commons-configuration</artifactId>
|
||||
<version>${commons.configuration.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-configuration2</artifactId>
|
||||
<version>${commons.configuration2.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons.lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
<version>${commons.beanutils.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commons.io.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-collections</groupId>
|
||||
<artifactId>commons-collections</artifactId>
|
||||
<version>${commons.collections.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>${commons.codec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>${jsr305.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>2.10.8</version>
|
||||
</dependency>
|
||||
|
||||
<!-- javassist -->
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<version>${javassist.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- jackson -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-jaxb-annotations</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||
<groupId>jakarta.xml.bind</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.jaxrs</groupId>
|
||||
<artifactId>jackson-jaxrs-base</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.jaxrs</groupId>
|
||||
<artifactId>jackson-jaxrs-json-provider</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-impl</artifactId>
|
||||
<version>${sun.xml.version}</version>
|
||||
<scope>runtime</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||
<groupId>jakarta.xml.bind</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>logging-interceptor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp-bom</artifactId>
|
||||
<version>${okhttp.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>pre-unit-test</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>post-unit-test</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.parent.build.directory}</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>apache-release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.concurrent;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class AtomicLock {
|
||||
|
||||
private static final Logger LOG = Log.logger(LockManager.class);
|
||||
|
||||
private String name;
|
||||
private AtomicReference<Thread> sign;
|
||||
|
||||
public AtomicLock(String name) {
|
||||
this.name = name;
|
||||
this.sign = new AtomicReference<>();
|
||||
}
|
||||
|
||||
public boolean tryLock() {
|
||||
Thread current = Thread.currentThread();
|
||||
return this.sign.compareAndSet(null, current);
|
||||
}
|
||||
|
||||
public void unlock() {
|
||||
if (this.sign.get() == null) {
|
||||
return;
|
||||
}
|
||||
Thread current = Thread.currentThread();
|
||||
if (!this.sign.compareAndSet(current, null)) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Thread '%s' trying to unlock '%s' " +
|
||||
"which is held by other threads now.",
|
||||
current.getName(), this.name));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean lock(int retries) {
|
||||
// The interval between retries is exponential growth, most wait
|
||||
// interval is 2^(retries-1)s. If retries=0, don't retry.
|
||||
if (retries < 0 || retries > 10) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Locking retry times should be in [0, 10], but got %d",
|
||||
retries));
|
||||
}
|
||||
|
||||
boolean isLocked = false;
|
||||
try {
|
||||
for (int i = 0; !(isLocked = this.tryLock()) && i < retries; i++) {
|
||||
Thread.sleep(1000 * (1L << i));
|
||||
}
|
||||
} catch (InterruptedException ignored) {
|
||||
LOG.info("Thread sleep is interrupted.");
|
||||
}
|
||||
return isLocked;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void name(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.concurrent;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
|
||||
public class BarrierEvent {
|
||||
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private final Condition cond = lock.newCondition();
|
||||
private volatile boolean signaled = false;
|
||||
|
||||
/**
|
||||
* Wait forever until the signal is received.
|
||||
* @throws InterruptedException if interrupted.
|
||||
*/
|
||||
public void await() throws InterruptedException {
|
||||
this.lock.lock();
|
||||
try {
|
||||
while (!this.signaled) {
|
||||
this.cond.await();
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait specified time in milliseconds.
|
||||
* @param timeout: the time in millisecond to wait.
|
||||
* @return true if signal is received, false if time out.
|
||||
* @throws InterruptedException if interrupted.
|
||||
*/
|
||||
public boolean await(long timeout) throws InterruptedException {
|
||||
E.checkArgument(timeout >= 0L,
|
||||
"The time must be >= 0, but got '%d'.",
|
||||
timeout);
|
||||
long deadline = System.currentTimeMillis() + timeout;
|
||||
this.lock.lock();
|
||||
try {
|
||||
while (!this.signaled) {
|
||||
timeout = deadline - System.currentTimeMillis();
|
||||
if (timeout > 0) {
|
||||
this.cond.await(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (System.currentTimeMillis() >= deadline) {
|
||||
return this.signaled;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.lock.lock();
|
||||
try {
|
||||
this.signaled = false;
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void signal() {
|
||||
this.lock.lock();
|
||||
try {
|
||||
this.signaled = true;
|
||||
this.cond.signal();
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void signalAll() {
|
||||
this.lock.lock();
|
||||
try {
|
||||
this.signaled = true;
|
||||
this.cond.signalAll();
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.util.concurrent.Striped;
|
||||
|
||||
/**
|
||||
* KeyLock provide an interface of segment lock
|
||||
*/
|
||||
public class KeyLock {
|
||||
|
||||
private Striped<Lock> locks;
|
||||
|
||||
public KeyLock() {
|
||||
// The default size is availableProcessors() * 4
|
||||
this(Runtime.getRuntime().availableProcessors() << 2);
|
||||
}
|
||||
|
||||
public KeyLock(int size) {
|
||||
this.locks = Striped.lock(size);
|
||||
}
|
||||
|
||||
private int indexOf(Lock lock) {
|
||||
for (int i = 0; i < this.locks.size(); i++) {
|
||||
if (this.locks.getAt(i) == lock) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock an object
|
||||
* @param key The object to lock
|
||||
* @return The lock(locked) of passed key
|
||||
*/
|
||||
public final Lock lock(Object key) {
|
||||
E.checkArgument(key != null, "Lock key can't be null");
|
||||
Lock lock = this.locks.get(key);
|
||||
lock.lock();
|
||||
return lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock an object
|
||||
* @param key The object to unlock
|
||||
*/
|
||||
public final void unlock(Object key) {
|
||||
E.checkArgument(key != null, "Unlock key can't be null");
|
||||
this.locks.get(key).unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock a list of object with sorted order
|
||||
* @param keys The objects to lock
|
||||
* @return The locks(locked) of keys
|
||||
*/
|
||||
public final List<Lock> lockAll(Object... keys) {
|
||||
E.checkArgument(keys != null && keys.length > 0,
|
||||
"Lock keys can't be null or empty");
|
||||
List<Lock> locks = new ArrayList<>(keys.length);
|
||||
for (Object key : keys) {
|
||||
E.checkArgument(key != null, "Lock key can't be null");
|
||||
Lock lock = this.locks.get(key);
|
||||
locks.add(lock);
|
||||
}
|
||||
locks.sort((a, b) -> {
|
||||
int diff = a.hashCode() - b.hashCode();
|
||||
if (diff == 0 && a != b) {
|
||||
diff = this.indexOf(a) - this.indexOf(b);
|
||||
assert diff != 0;
|
||||
}
|
||||
return diff;
|
||||
});
|
||||
for (Lock lock : locks) {
|
||||
lock.lock();
|
||||
}
|
||||
return Collections.unmodifiableList(locks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock two objects with sorted order
|
||||
* NOTE: This is to optimize the performance of lockAll(keys)
|
||||
* @param key1 The first object
|
||||
* @param key2 The second object
|
||||
* @return locks for the two objects
|
||||
*/
|
||||
public List<Lock> lockAll(Object key1, Object key2) {
|
||||
E.checkArgument(key1 != null, "Lock key can't be null");
|
||||
E.checkArgument(key2 != null, "Lock key can't be null");
|
||||
Lock lock1 = this.locks.get(key1);
|
||||
Lock lock2 = this.locks.get(key2);
|
||||
|
||||
int diff = lock1.hashCode() - lock2.hashCode();
|
||||
if (diff == 0 && lock1 != lock2) {
|
||||
diff = this.indexOf(lock1) - this.indexOf(lock2);
|
||||
assert diff != 0;
|
||||
}
|
||||
|
||||
List<Lock> locks = diff > 0 ?
|
||||
ImmutableList.of(lock2, lock1) :
|
||||
ImmutableList.of(lock1, lock2);
|
||||
|
||||
for (Lock lock : locks) {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
return locks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock a list of object
|
||||
* @param locks The locks to unlock
|
||||
*/
|
||||
public final void unlockAll(List<Lock> locks) {
|
||||
E.checkArgument(locks != null, "Unlock locks can't be null");
|
||||
for (int i = locks.size(); i > 0; i--) {
|
||||
assert this.indexOf(locks.get(i - 1)) != -1;
|
||||
locks.get(i - 1).unlock();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.concurrent;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
public class LockGroup {
|
||||
|
||||
private final String name;
|
||||
private final Map<String, Object> locksMap;
|
||||
|
||||
public LockGroup(String lockGroup) {
|
||||
this.name = lockGroup;
|
||||
this.locksMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public Lock lock(String lockName) {
|
||||
if (!this.locksMap.containsKey(lockName)) {
|
||||
this.locksMap.putIfAbsent(lockName, new ReentrantLock());
|
||||
}
|
||||
return (Lock) this.locksMap.get(lockName);
|
||||
}
|
||||
|
||||
public AtomicLock atomicLock(String lockName) {
|
||||
if (!this.locksMap.containsKey(lockName)) {
|
||||
this.locksMap.putIfAbsent(lockName, new AtomicLock(lockName));
|
||||
}
|
||||
return (AtomicLock) this.locksMap.get(lockName);
|
||||
}
|
||||
|
||||
public ReadWriteLock readWriteLock(String lockName) {
|
||||
if (!this.locksMap.containsKey(lockName)) {
|
||||
this.locksMap.putIfAbsent(lockName, new ReentrantReadWriteLock());
|
||||
}
|
||||
return (ReadWriteLock) this.locksMap.get(lockName);
|
||||
}
|
||||
|
||||
public KeyLock keyLock(String lockName) {
|
||||
if (!this.locksMap.containsKey(lockName)) {
|
||||
this.locksMap.putIfAbsent(lockName, new KeyLock());
|
||||
}
|
||||
return (KeyLock) this.locksMap.get(lockName);
|
||||
}
|
||||
|
||||
public KeyLock keyLock(String lockName, int size) {
|
||||
if (!this.locksMap.containsKey(lockName)) {
|
||||
this.locksMap.putIfAbsent(lockName, new KeyLock(size));
|
||||
}
|
||||
return (KeyLock) this.locksMap.get(lockName);
|
||||
}
|
||||
|
||||
public <K extends Comparable<K>> RowLock<K> rowLock(String lockName) {
|
||||
if (!this.locksMap.containsKey(lockName)) {
|
||||
this.locksMap.putIfAbsent(lockName, new RowLock<>());
|
||||
}
|
||||
Object value = this.locksMap.get(lockName);
|
||||
@SuppressWarnings("unchecked")
|
||||
RowLock<K> lock = (RowLock<K>) value;
|
||||
return lock;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.concurrent;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class LockManager {
|
||||
|
||||
private static final LockManager INSTANCE = new LockManager();
|
||||
|
||||
public static LockManager instance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private Map<String, LockGroup> lockGroupMap;
|
||||
|
||||
private LockManager() {
|
||||
this.lockGroupMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public boolean exists(String group) {
|
||||
return this.lockGroupMap.containsKey(group);
|
||||
}
|
||||
|
||||
public LockGroup create(String group) {
|
||||
if (exists(group)) {
|
||||
throw new RuntimeException(String.format(
|
||||
"LockGroup '%s' already exists", group));
|
||||
}
|
||||
LockGroup lockGroup = new LockGroup(group);
|
||||
LockGroup previous = this.lockGroupMap.putIfAbsent(group, lockGroup);
|
||||
if (previous != null) {
|
||||
return previous;
|
||||
}
|
||||
return lockGroup;
|
||||
}
|
||||
|
||||
public LockGroup get(String group) {
|
||||
LockGroup lockGroup = this.lockGroupMap.get(group);
|
||||
if (lockGroup == null) {
|
||||
throw new RuntimeException(String.format(
|
||||
"LockGroup '%s' does not exists", group));
|
||||
}
|
||||
return lockGroup;
|
||||
}
|
||||
|
||||
public void destroy(String group) {
|
||||
if (this.exists(group)) {
|
||||
this.lockGroupMap.remove(group);
|
||||
} else {
|
||||
throw new RuntimeException(String.format(
|
||||
"LockGroup '%s' does not exists", group));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.concurrent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class PausableScheduledThreadPool extends ScheduledThreadPoolExecutor {
|
||||
|
||||
private static final Logger LOG = Log.logger(PausableScheduledThreadPool.class);
|
||||
|
||||
private volatile boolean paused = false;
|
||||
|
||||
public PausableScheduledThreadPool(int corePoolSize,
|
||||
ThreadFactory factory) {
|
||||
super(corePoolSize, factory);
|
||||
}
|
||||
|
||||
public synchronized void pauseSchedule() {
|
||||
this.paused = true;
|
||||
LOG.info("PausableScheduledThreadPool was paused");
|
||||
}
|
||||
|
||||
public synchronized void resumeSchedule() {
|
||||
this.paused = false;
|
||||
this.notifyAll();
|
||||
LOG.info("PausableScheduledThreadPool was resumed");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeExecute(Thread t, Runnable r) {
|
||||
synchronized (this) {
|
||||
while (this.paused) {
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {
|
||||
LOG.warn("PausableScheduledThreadPool was interrupted");
|
||||
}
|
||||
}
|
||||
}
|
||||
super.beforeExecute(t, r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (this.paused) {
|
||||
this.resumeSchedule();
|
||||
}
|
||||
super.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
if (this.paused) {
|
||||
this.resumeSchedule();
|
||||
}
|
||||
return super.shutdownNow();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
|
||||
public class RowLock<K extends Comparable<K>> {
|
||||
|
||||
private final Map<K, Lock> locks = new ConcurrentHashMap<>();
|
||||
private final ThreadLocal<Map<K, LocalLock>> localLocks =
|
||||
ThreadLocal.withInitial(HashMap::new);
|
||||
|
||||
public void lock(K key) {
|
||||
E.checkArgument(key != null, "Lock key can't be null");
|
||||
LocalLock localLock = this.localLocks.get().get(key);
|
||||
if (localLock != null) {
|
||||
localLock.lockCount++;
|
||||
} else {
|
||||
Lock current = new ReentrantLock();
|
||||
Lock previous = this.locks.putIfAbsent(key, current);
|
||||
if (previous != null) {
|
||||
current = previous;
|
||||
}
|
||||
current.lock();
|
||||
this.localLocks.get().put(key, new LocalLock(current));
|
||||
}
|
||||
}
|
||||
|
||||
public void unlock(K key) {
|
||||
E.checkArgument(key != null, "Unlock key can't be null");
|
||||
LocalLock localLock = this.localLocks.get().get(key);
|
||||
if (localLock == null) {
|
||||
return;
|
||||
}
|
||||
if (--localLock.lockCount == 0) {
|
||||
this.locks.remove(key, localLock.current);
|
||||
this.localLocks.get().remove(key);
|
||||
localLock.current.unlock();
|
||||
}
|
||||
E.checkState(localLock.lockCount >= 0,
|
||||
"The lock count must be >= 0, but got %s",
|
||||
localLock.lockCount);
|
||||
}
|
||||
|
||||
public void lockAll(Set<K> keys) {
|
||||
E.checkArgument(keys != null && keys.size() > 0,
|
||||
"Lock keys can't be null or empty");
|
||||
List<K> list = new ArrayList<>(keys);
|
||||
Collections.sort(list);
|
||||
for (K key : list) {
|
||||
this.lock(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void unlockAll(Set<K> keys) {
|
||||
E.checkArgument(keys != null && keys.size() > 0,
|
||||
"Unlock keys can't be null or empty");
|
||||
for (K key : keys) {
|
||||
this.unlock(key);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LocalLock {
|
||||
|
||||
private final Lock current;
|
||||
private int lockCount;
|
||||
|
||||
private LocalLock(Lock current) {
|
||||
this.current = current;
|
||||
this.lockCount = 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.config;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
public class ConfigConvOption<T, R> extends TypedOption<T, R> {
|
||||
|
||||
private final Function<T, R> converter;
|
||||
|
||||
public ConfigConvOption(String name, String desc, Predicate<T> pred,
|
||||
Function<T, R> convert, T value) {
|
||||
this(name, false, desc, pred, convert, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ConfigConvOption(String name, boolean required, String desc,
|
||||
Predicate<T> pred, Function<T, R> convert,
|
||||
T value) {
|
||||
super(name, required, desc, pred, (Class<T>) value.getClass(), value);
|
||||
E.checkNotNull(convert, "convert");
|
||||
this.converter = convert;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R convert(T value) {
|
||||
return this.converter.apply(value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.config;
|
||||
|
||||
public class ConfigException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -8711375282196157058L;
|
||||
|
||||
public ConfigException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ConfigException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ConfigException(String message, Object... args) {
|
||||
super(String.format(message, args));
|
||||
}
|
||||
|
||||
public ConfigException(String message, Throwable cause, Object... args) {
|
||||
super(String.format(message, args), cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
public class ConfigListConvOption<T, R> extends TypedOption<List<T>, List<R>> {
|
||||
|
||||
private final Class<T> elemClass;
|
||||
private final Function<T, R> converter;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ConfigListConvOption(String name, String desc,
|
||||
Predicate<List<T>> pred, Function<T, R> convert,
|
||||
T... values) {
|
||||
this(name, false, desc, pred, convert, null, Arrays.asList(values));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ConfigListConvOption(String name, boolean required, String desc,
|
||||
Predicate<List<T>> pred, Function<T, R> convert,
|
||||
Class<T> clazz, List<T> values) {
|
||||
super(name, required, desc, pred,
|
||||
(Class<List<T>>) values.getClass(), values);
|
||||
E.checkNotNull(convert, "convert");
|
||||
if (clazz == null && values.size() > 0) {
|
||||
clazz = (Class<T>) values.get(0).getClass();
|
||||
}
|
||||
E.checkArgumentNotNull(clazz, "Element class can't be null");
|
||||
this.elemClass = clazz;
|
||||
this.converter = convert;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean forList() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<T> parse(String value) {
|
||||
return ConfigListOption.convert(value, part -> {
|
||||
return this.parse(part, this.elemClass);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<R> convert(List<T> values) {
|
||||
List<R> results = new ArrayList<>(values.size());
|
||||
for (T value : values) {
|
||||
results.add(this.converter.apply(value));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
public class ConfigListOption<T> extends ConfigOption<List<T>> {
|
||||
|
||||
private final Class<T> elemClass;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ConfigListOption(String name, String desc,
|
||||
Predicate<List<T>> pred, T... values) {
|
||||
this(name, false, desc, pred, null, Arrays.asList(values));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ConfigListOption(String name, boolean required, String desc,
|
||||
Predicate<List<T>> pred, Class<T> clazz,
|
||||
List<T> values) {
|
||||
super(name, required, desc, pred,
|
||||
(Class<List<T>>) values.getClass(), values);
|
||||
if (clazz == null && values.size() > 0) {
|
||||
clazz = (Class<T>) values.get(0).getClass();
|
||||
}
|
||||
E.checkArgumentNotNull(clazz, "Element class can't be null");
|
||||
this.elemClass = clazz;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean forList() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<T> parse(String value) {
|
||||
return convert(value, part -> this.parse(part, this.elemClass));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> List<T> convert(Object value, Function<String, ?> conv) {
|
||||
if (value instanceof List) {
|
||||
return (List<T>) value;
|
||||
}
|
||||
// If target data type is List, parse it as a list
|
||||
String str = (String) value;
|
||||
if (str.startsWith("[") && str.endsWith("]")) {
|
||||
str = str.substring(1, str.length() - 1);
|
||||
}
|
||||
|
||||
String[] parts = str.split(",");
|
||||
List<T> results = new ArrayList<>(parts.length);
|
||||
for (String part : parts) {
|
||||
results.add((T) conv.apply(part.trim()));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.config;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
public class ConfigOption<T> extends TypedOption<T, T> {
|
||||
|
||||
public ConfigOption(String name, String desc, T value) {
|
||||
this(name, desc, null, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ConfigOption(String name, String desc, Predicate<T> pred, T value) {
|
||||
this(name, false, desc, pred, (Class<T>) value.getClass(), value);
|
||||
}
|
||||
|
||||
public ConfigOption(String name, boolean required, String desc,
|
||||
Predicate<T> pred, Class<T> type, T value) {
|
||||
super(name, required, desc, pred, type, value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.configuration2.Configuration;
|
||||
import org.apache.commons.configuration2.FileBasedConfiguration;
|
||||
import org.apache.commons.configuration2.PropertiesConfiguration;
|
||||
import org.apache.commons.configuration2.YAMLConfiguration;
|
||||
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
|
||||
import org.apache.commons.configuration2.builder.fluent.Configurations;
|
||||
import org.apache.commons.configuration2.builder.fluent.Parameters;
|
||||
import org.apache.commons.configuration2.ex.ConfigurationException;
|
||||
import org.apache.commons.configuration2.io.FileHandler;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class HugeConfig extends PropertiesConfiguration {
|
||||
|
||||
private static final Logger LOG = Log.logger(HugeConfig.class);
|
||||
|
||||
private String configPath;
|
||||
|
||||
public HugeConfig(Configuration config) {
|
||||
loadConfig(config);
|
||||
this.configPath = null;
|
||||
}
|
||||
|
||||
public HugeConfig(String configFile) {
|
||||
loadConfig(loadConfigFile(configFile));
|
||||
this.configPath = configFile;
|
||||
}
|
||||
|
||||
public HugeConfig(Map<String, Object> propertyMap) {
|
||||
if (propertyMap == null) {
|
||||
throw new ConfigException("The property map is null");
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Object> kv : propertyMap.entrySet()) {
|
||||
this.addProperty(kv.getKey(), kv.getValue());
|
||||
}
|
||||
this.checkRequiredOptions();
|
||||
}
|
||||
|
||||
private void loadConfig(Configuration config) {
|
||||
if (config == null) {
|
||||
throw new ConfigException("The config object is null");
|
||||
}
|
||||
this.setLayoutIfNeeded(config);
|
||||
|
||||
this.append(config);
|
||||
this.checkRequiredOptions();
|
||||
}
|
||||
|
||||
private void setLayoutIfNeeded(Configuration conf) {
|
||||
if (!(conf instanceof PropertiesConfiguration)) {
|
||||
return;
|
||||
}
|
||||
PropertiesConfiguration propConf = (PropertiesConfiguration) conf;
|
||||
this.setLayout(propConf.getLayout());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T, R> R get(TypedOption<T, R> option) {
|
||||
Object value = this.getProperty(option.name());
|
||||
if (value == null) {
|
||||
return option.defaultValue();
|
||||
}
|
||||
return (R) value;
|
||||
}
|
||||
|
||||
public Map<String, String> getMap(ConfigListOption<String> option) {
|
||||
List<String> values = this.get(option);
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (String value : values) {
|
||||
String[] pair = value.split(":", 2);
|
||||
E.checkState(pair.length == 2,
|
||||
"Invalid option format for '%s': %s(expect KEY:VALUE)",
|
||||
option.name(), value);
|
||||
result.put(pair[0].trim(), pair[1].trim());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertyDirect(String key, Object value) {
|
||||
TypedOption<?, ?> option = OptionSpace.get(key);
|
||||
if (option == null) {
|
||||
LOG.warn("The config option '{}' is redundant, " +
|
||||
"please ensure it has been registered", key);
|
||||
} else {
|
||||
// The input value is String(parsed by PropertiesConfiguration)
|
||||
value = this.validateOption(key, value);
|
||||
}
|
||||
if (this.containsKey(key) && value instanceof List) {
|
||||
for (Object item : (List<?>) value) {
|
||||
super.addPropertyDirect(key, item);
|
||||
}
|
||||
} else {
|
||||
super.addPropertyDirect(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addPropertyInternal(String key, Object value) {
|
||||
this.addPropertyDirect(key, value);
|
||||
}
|
||||
|
||||
private Object validateOption(String key, Object value) {
|
||||
TypedOption<?, ?> option = OptionSpace.get(key);
|
||||
|
||||
if (value instanceof String) {
|
||||
return option.parseConvert((String) value);
|
||||
}
|
||||
|
||||
Class<?> dataType = option.dataType();
|
||||
if (dataType.isInstance(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid value for key '%s': '%s'", key, value));
|
||||
}
|
||||
|
||||
private void checkRequiredOptions() {
|
||||
// TODO: Check required options must be contained in this map
|
||||
}
|
||||
|
||||
public void save(File copiedFile) throws ConfigurationException {
|
||||
FileHandler fileHandler = new FileHandler(this);
|
||||
fileHandler.save(copiedFile);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public File file() {
|
||||
if (StringUtils.isEmpty(this.configPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new File(this.configPath);
|
||||
}
|
||||
|
||||
public void file(String path) {
|
||||
this.configPath = path;
|
||||
}
|
||||
|
||||
private static Configuration loadConfigFile(String path) {
|
||||
E.checkNotNull(path, "config path");
|
||||
E.checkArgument(!path.isEmpty(),
|
||||
"The config path can't be empty");
|
||||
|
||||
File file = new File(path);
|
||||
return loadConfigFile(file);
|
||||
}
|
||||
|
||||
private static Configuration loadConfigFile(File configFile) {
|
||||
E.checkArgument(configFile.exists() &&
|
||||
configFile.isFile() &&
|
||||
configFile.canRead(),
|
||||
"Please specify a proper config file rather than: '%s'",
|
||||
configFile.toString());
|
||||
|
||||
try {
|
||||
String fileName = configFile.getName();
|
||||
String fileExtension = FilenameUtils.getExtension(fileName);
|
||||
|
||||
Configuration config;
|
||||
Configurations configs = new Configurations();
|
||||
|
||||
switch (fileExtension) {
|
||||
case "yml":
|
||||
case "yaml":
|
||||
Parameters params = new Parameters();
|
||||
FileBasedConfigurationBuilder<FileBasedConfiguration> builder =
|
||||
new FileBasedConfigurationBuilder(YAMLConfiguration.class)
|
||||
.configure(params.fileBased().setFile(configFile));
|
||||
config = builder.getConfiguration();
|
||||
break;
|
||||
case "xml":
|
||||
config = configs.xml(configFile);
|
||||
break;
|
||||
default:
|
||||
config = configs.properties(configFile);
|
||||
break;
|
||||
}
|
||||
return config;
|
||||
} catch (ConfigurationException e) {
|
||||
throw new ConfigException("Unable to load config: '%s'",
|
||||
e, configFile);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.config;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
public final class OptionChecker {
|
||||
|
||||
public static <O> Predicate<O> disallowEmpty() {
|
||||
return o -> {
|
||||
if (o == null) {
|
||||
return false;
|
||||
}
|
||||
if (o instanceof String) {
|
||||
return StringUtils.isNotBlank((String) o);
|
||||
}
|
||||
if (o.getClass().isArray() && (Array.getLength(o) == 0)) {
|
||||
return false;
|
||||
}
|
||||
return !(o instanceof Iterable) || ((Iterable<?>) o).iterator().hasNext();
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <O> Predicate<O> allowValues(O... values) {
|
||||
return o -> o != null && Arrays.asList(values).contains(o);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <O> Predicate<List<O>> inValues(O... values) {
|
||||
return o -> o != null && new HashSet<>(Arrays.asList(values)).containsAll(o);
|
||||
}
|
||||
|
||||
public static <N extends Number> Predicate<N> positiveInt() {
|
||||
return number -> number != null && number.longValue() > 0;
|
||||
}
|
||||
|
||||
public static <N extends Number> Predicate<N> nonNegativeInt() {
|
||||
return number -> number != null && number.longValue() >= 0;
|
||||
}
|
||||
|
||||
public static <N extends Number> Predicate<N> rangeInt(N min, N max) {
|
||||
return number -> {
|
||||
if (number == null) {
|
||||
return false;
|
||||
}
|
||||
long value = number.longValue();
|
||||
return value >= min.longValue() && value <= max.longValue();
|
||||
};
|
||||
}
|
||||
|
||||
public static <N extends Number> Predicate<N> rangeDouble(N min, N max) {
|
||||
return number -> {
|
||||
if (number == null) {
|
||||
return false;
|
||||
}
|
||||
double value = number.doubleValue();
|
||||
return value >= min.doubleValue() && value <= max.doubleValue();
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.config;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class OptionHolder {
|
||||
|
||||
private static final Logger LOG = Log.logger(HugeConfig.class);
|
||||
|
||||
protected Map<String, TypedOption<?, ?>> options;
|
||||
|
||||
public OptionHolder() {
|
||||
this.options = new HashMap<>();
|
||||
}
|
||||
|
||||
protected void registerOptions() {
|
||||
for (Field field : this.getClass().getFields()) {
|
||||
if (!TypedOption.class.isAssignableFrom(field.getType())) {
|
||||
// Skip if not option
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
TypedOption<?, ?> option = (TypedOption<?, ?>) field.get(this);
|
||||
// Fields of subclass first, don't overwrite by superclass
|
||||
this.options.putIfAbsent(option.name(), option);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to register option: {}", field, e);
|
||||
throw new ConfigException(
|
||||
"Failed to register option: %s", field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, TypedOption<?, ?>> options() {
|
||||
return Collections.unmodifiableMap(this.options);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.config;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public final class OptionSpace {
|
||||
|
||||
private static final Logger LOG = Log.logger(OptionSpace.class);
|
||||
|
||||
private static final Map<String, Class<? extends OptionHolder>> HOLDERS;
|
||||
private static final Map<String, TypedOption<?, ?>> OPTIONS;
|
||||
private static final String INSTANCE_METHOD = "instance";
|
||||
|
||||
static {
|
||||
HOLDERS = new ConcurrentHashMap<>();
|
||||
OPTIONS = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public static void register(String module, String holder) {
|
||||
ClassLoader classLoader = OptionSpace.class.getClassLoader();
|
||||
Class<?> clazz;
|
||||
try {
|
||||
clazz = classLoader.loadClass(holder);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ConfigException(
|
||||
"Failed to load class of option holder '%s'", e, holder);
|
||||
}
|
||||
|
||||
// Check subclass
|
||||
if (!OptionHolder.class.isAssignableFrom(clazz)) {
|
||||
throw new ConfigException(
|
||||
"Class '%s' is not a subclass of OptionHolder", holder);
|
||||
}
|
||||
|
||||
OptionHolder instance = null;
|
||||
Exception exception = null;
|
||||
try {
|
||||
Method method = clazz.getMethod(INSTANCE_METHOD);
|
||||
if (!Modifier.isStatic(method.getModifiers())) {
|
||||
throw new NoSuchMethodException(INSTANCE_METHOD);
|
||||
}
|
||||
instance = (OptionHolder) method.invoke(null);
|
||||
if (instance == null) {
|
||||
exception = new ConfigException(
|
||||
"Returned null from %s() method",
|
||||
INSTANCE_METHOD);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
LOG.warn("Class {} does not has static method {}.",
|
||||
holder, INSTANCE_METHOD);
|
||||
exception = e;
|
||||
} catch (InvocationTargetException e) {
|
||||
LOG.warn("Can't call static method {} from class {}.",
|
||||
INSTANCE_METHOD, holder);
|
||||
exception = e;
|
||||
} catch (IllegalAccessException e) {
|
||||
LOG.warn("Illegal access while calling method {} from class {}.",
|
||||
INSTANCE_METHOD, holder);
|
||||
exception = e;
|
||||
}
|
||||
|
||||
if (exception != null) {
|
||||
throw new ConfigException("Failed to instantiate option holder: %s",
|
||||
exception, holder);
|
||||
}
|
||||
|
||||
register(module, instance);
|
||||
}
|
||||
|
||||
public static void register(String module, OptionHolder holder) {
|
||||
// Check exists
|
||||
if (HOLDERS.containsKey(module)) {
|
||||
LOG.warn("Already registered option holder: {} ({})",
|
||||
module, HOLDERS.get(module));
|
||||
}
|
||||
E.checkArgumentNotNull(holder, "OptionHolder can't be null");
|
||||
HOLDERS.put(module, holder.getClass());
|
||||
OPTIONS.putAll(holder.options());
|
||||
LOG.debug("Registered options for OptionHolder: {}",
|
||||
holder.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
public static Set<String> keys() {
|
||||
return Collections.unmodifiableSet(OPTIONS.keySet());
|
||||
}
|
||||
|
||||
public static boolean containKey(String key) {
|
||||
return OPTIONS.containsKey(key);
|
||||
}
|
||||
|
||||
public static TypedOption<?, ?> get(String key) {
|
||||
return OPTIONS.get(key);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.config;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.commons.configuration.PropertyConverter;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
public class TypedOption<T, R> {
|
||||
|
||||
private static final Logger LOG = Log.logger(TypedOption.class);
|
||||
|
||||
private static final Set<Class<?>> ACCEPTED_DATA_TYPES;
|
||||
private static final String ACCEPTED_DATA_TYPES_STRING;
|
||||
|
||||
static {
|
||||
ACCEPTED_DATA_TYPES = ImmutableSet.of(
|
||||
Boolean.class,
|
||||
Short.class,
|
||||
Integer.class,
|
||||
Byte.class,
|
||||
Long.class,
|
||||
Float.class,
|
||||
Double.class,
|
||||
String.class,
|
||||
String[].class,
|
||||
Class.class,
|
||||
List.class
|
||||
);
|
||||
|
||||
ACCEPTED_DATA_TYPES_STRING = Joiner.on(", ").join(ACCEPTED_DATA_TYPES);
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final String desc;
|
||||
private final boolean required;
|
||||
private final Class<T> dataType;
|
||||
private final T defaultValue;
|
||||
private final Predicate<T> checkFunc;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public TypedOption(String name, boolean required, String desc,
|
||||
Predicate<T> pred, Class<T> type, T value) {
|
||||
E.checkNotNull(name, "name");
|
||||
E.checkNotNull(type, "dataType");
|
||||
|
||||
this.name = name;
|
||||
this.dataType = (Class<T>) this.checkAndAssignDataType(type);
|
||||
this.defaultValue = value;
|
||||
this.required = required;
|
||||
this.desc = desc;
|
||||
this.checkFunc = pred;
|
||||
|
||||
this.check(this.defaultValue);
|
||||
}
|
||||
|
||||
private Class<?> checkAndAssignDataType(Class<T> dataType) {
|
||||
for (Class<?> clazz : ACCEPTED_DATA_TYPES) {
|
||||
if (clazz.isAssignableFrom(dataType)) {
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
|
||||
String msg = String.format("Input data type '%s' doesn't belong " +
|
||||
"to acceptable type set: [%s]",
|
||||
dataType, ACCEPTED_DATA_TYPES_STRING);
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public Class<T> dataType() {
|
||||
return this.dataType;
|
||||
}
|
||||
|
||||
public String desc() {
|
||||
return this.desc;
|
||||
}
|
||||
|
||||
public boolean required() {
|
||||
return this.required;
|
||||
}
|
||||
|
||||
public R defaultValue() {
|
||||
return this.convert(this.defaultValue);
|
||||
}
|
||||
|
||||
public R parseConvert(String value) {
|
||||
T parsed = this.parse(value);
|
||||
this.check(parsed);
|
||||
return this.convert(parsed);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T parse(String value) {
|
||||
return (T) this.parse(value, this.dataType);
|
||||
}
|
||||
|
||||
protected Object parse(String value, Class<?> dataType) {
|
||||
if (dataType.equals(String.class)) {
|
||||
return value;
|
||||
} else if (dataType.equals(Class.class)) {
|
||||
try {
|
||||
if (value.startsWith("class")) {
|
||||
value = value.substring("class".length()).trim();
|
||||
}
|
||||
return Class.forName(value);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ConfigException(
|
||||
"Failed to parse Class from String '%s'", e, value);
|
||||
}
|
||||
} else if (List.class.isAssignableFrom(dataType)) {
|
||||
E.checkState(this.forList(),
|
||||
"List option can't be registered with class %s",
|
||||
this.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
// Use PropertyConverter method `toXXX` convert value
|
||||
String methodTo = "to" + dataType.getSimpleName();
|
||||
try {
|
||||
Method method = PropertyConverter.class.getMethod(
|
||||
methodTo, Object.class);
|
||||
return method.invoke(null, value);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
LOG.error("Invalid type of value '{}' for option '{}'",
|
||||
value, this.name, e);
|
||||
throw new ConfigException(
|
||||
"Invalid type of value '%s' for option '%s', " +
|
||||
"expect '%s' type",
|
||||
value, this.name, dataType.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
protected void check(Object value) {
|
||||
E.checkNotNull(value, "value", this.name);
|
||||
if (!this.dataType.isInstance(value)) {
|
||||
throw new ConfigException(
|
||||
"Invalid type of value '%s' for option '%s', " +
|
||||
"expect type %s but got %s", value, this.name,
|
||||
this.dataType.getSimpleName(),
|
||||
value.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
if (this.checkFunc != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) value;
|
||||
if (!this.checkFunc.apply(result)) {
|
||||
throw new ConfigException("Invalid option value for '%s': %s",
|
||||
this.name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected R convert(T value) {
|
||||
return (R) value;
|
||||
}
|
||||
|
||||
protected boolean forList() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[%s]%s=%s", this.dataType.getSimpleName(),
|
||||
this.name, this.defaultValue);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.date;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* The SafeDateFormat actually is a proxy for joda DateTimeFormatter
|
||||
*/
|
||||
public class SafeDateFormat {
|
||||
|
||||
private static final int ONE_HOUR_MS = 3600 * 1000;
|
||||
|
||||
private final String pattern;
|
||||
private DateTimeFormatter formatter;
|
||||
|
||||
public SafeDateFormat(String pattern) {
|
||||
this.pattern = pattern;
|
||||
this.formatter = DateTimeFormat.forPattern(pattern);
|
||||
}
|
||||
|
||||
public synchronized void setTimeZone(String zoneId) {
|
||||
int hoursOffset = TimeZone.getTimeZone(zoneId).getRawOffset() /
|
||||
ONE_HOUR_MS;
|
||||
DateTimeZone zone = DateTimeZone.forOffsetHours(hoursOffset);
|
||||
this.formatter = this.formatter.withZone(zone);
|
||||
}
|
||||
|
||||
public TimeZone getTimeZone() {
|
||||
return this.formatter.getZone().toTimeZone();
|
||||
}
|
||||
|
||||
public Date parse(String source) {
|
||||
return this.formatter.parseDateTime(source).toDate();
|
||||
}
|
||||
|
||||
public String format(Date date) {
|
||||
return this.formatter.print(date.getTime());
|
||||
}
|
||||
|
||||
public Object toPattern() {
|
||||
return this.pattern;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.event;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
|
||||
public class Event extends java.util.EventObject {
|
||||
|
||||
private static final long serialVersionUID = 1625973849208342813L;
|
||||
|
||||
private String name;
|
||||
private Object[] args;
|
||||
|
||||
public Event(Object source, String event) {
|
||||
this(source, event, Collections.emptyList().toArray());
|
||||
}
|
||||
|
||||
public Event(Object source, String event, Object... args) {
|
||||
super(source);
|
||||
this.name = event;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public Object[] args() {
|
||||
return this.args;
|
||||
}
|
||||
|
||||
public void checkArgs(Class<?>... classes) throws IllegalArgumentException {
|
||||
E.checkArgument(this.args.length == classes.length,
|
||||
"The args count of event '%s' should be %s(actual %s)",
|
||||
this.name, classes.length, this.args.length);
|
||||
int i = 0;
|
||||
for (Class<?> c : classes) {
|
||||
Object arg = this.args[i++];
|
||||
if (arg == null) {
|
||||
continue;
|
||||
}
|
||||
E.checkArgument(c.isAssignableFrom(arg.getClass()),
|
||||
"The arg '%s'(%s) can't match %s",
|
||||
arg, arg.getClass(), c);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Event{name='%s', args=%s}",
|
||||
this.name, Arrays.asList(this.args));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.event;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.ExecutorUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.iterator.ExtendableIterator;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class EventHub {
|
||||
|
||||
private static final Logger LOG = Log.logger(EventHub.class);
|
||||
|
||||
public static final String EVENT_WORKER = "event-worker-%d";
|
||||
public static final String ANY_EVENT = "*";
|
||||
|
||||
private static final List<EventListener> EMPTY = ImmutableList.of();
|
||||
|
||||
// Event executor
|
||||
private static ExecutorService executor = null;
|
||||
|
||||
private String name;
|
||||
private Map<String, List<EventListener>> listeners;
|
||||
|
||||
public EventHub() {
|
||||
this("hub");
|
||||
}
|
||||
|
||||
public EventHub(String name) {
|
||||
this(name, 1, Runtime.getRuntime().availableProcessors() << 2);
|
||||
}
|
||||
|
||||
public EventHub(String name, int threadSize) {
|
||||
LOG.debug("Create new EventHub {},threadSize {}", name, threadSize);
|
||||
this.name = name;
|
||||
this.listeners = new ConcurrentHashMap<>();
|
||||
EventHub.init(threadSize);
|
||||
}
|
||||
|
||||
public EventHub(String name, int corePoolSize, int maximumPoolSize) {
|
||||
LOG.debug("Create new EventHub {},corePoolSize {}, maximumPoolSize {}", name, corePoolSize, maximumPoolSize);
|
||||
this.name = name;
|
||||
this.listeners = new ConcurrentHashMap<>();
|
||||
EventHub.init(corePoolSize, maximumPoolSize);
|
||||
}
|
||||
|
||||
public static synchronized void init(int poolSize) {
|
||||
if (executor != null) {
|
||||
return;
|
||||
}
|
||||
LOG.debug("Init pool(size {}) for EventHub", poolSize);
|
||||
executor = ExecutorUtil.newFixedThreadPool(poolSize, EVENT_WORKER);
|
||||
}
|
||||
|
||||
public static synchronized void init(int corePoolSize, int maximumPoolSize) {
|
||||
LOG.debug("Init corePoolSize {}, maximumPoolSize {} for EventHub", corePoolSize, maximumPoolSize);
|
||||
if (executor != null) {
|
||||
LOG.debug("EventHub executor already initialized");
|
||||
return;
|
||||
}
|
||||
executor = ExecutorUtil.newDynamicThreadExecutor(EVENT_WORKER, corePoolSize, maximumPoolSize);
|
||||
}
|
||||
|
||||
public static synchronized boolean destroy(long timeout)
|
||||
throws InterruptedException {
|
||||
E.checkState(executor != null, "EventHub has not been initialized");
|
||||
LOG.debug("Destroy pool for EventHub");
|
||||
executor.shutdown();
|
||||
return executor.awaitTermination(timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private static ExecutorService executor() {
|
||||
ExecutorService e = executor;
|
||||
E.checkState(e != null, "The event executor has been destroyed");
|
||||
return e;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public boolean containsListener(String event) {
|
||||
List<EventListener> ls = this.listeners.get(event);
|
||||
return ls != null && ls.size() > 0;
|
||||
}
|
||||
|
||||
public List<EventListener> listeners(String event) {
|
||||
List<EventListener> ls = this.listeners.get(event);
|
||||
return ls == null ? EMPTY : Collections.unmodifiableList(ls);
|
||||
}
|
||||
|
||||
public void listen(String event, EventListener listener) {
|
||||
E.checkNotNull(event, "event");
|
||||
E.checkNotNull(listener, "event listener");
|
||||
|
||||
if (!this.listeners.containsKey(event)) {
|
||||
this.listeners.putIfAbsent(event, new CopyOnWriteArrayList<>());
|
||||
}
|
||||
List<EventListener> ls = this.listeners.get(event);
|
||||
assert ls != null : this.listeners;
|
||||
ls.add(listener);
|
||||
}
|
||||
|
||||
public List<EventListener> unlisten(String event) {
|
||||
List<EventListener> ls = this.listeners.remove(event);
|
||||
return ls == null ? EMPTY : Collections.unmodifiableList(ls);
|
||||
}
|
||||
|
||||
public int unlisten(String event, EventListener listener) {
|
||||
List<EventListener> ls = this.listeners.get(event);
|
||||
if (ls == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
while (ls.remove(listener)) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public Future<Integer> notify(String event, @Nullable Object... args) {
|
||||
@SuppressWarnings("resource")
|
||||
ExtendableIterator<EventListener> all = new ExtendableIterator<>();
|
||||
|
||||
List<EventListener> ls = this.listeners.get(event);
|
||||
if (ls != null && !ls.isEmpty()) {
|
||||
all.extend(ls.iterator());
|
||||
}
|
||||
List<EventListener> lsAny = this.listeners.get(ANY_EVENT);
|
||||
if (lsAny != null && !lsAny.isEmpty()) {
|
||||
all.extend(lsAny.iterator());
|
||||
}
|
||||
|
||||
if (!all.hasNext()) {
|
||||
return CompletableFuture.completedFuture(0);
|
||||
}
|
||||
|
||||
Event ev = new Event(this, event, args);
|
||||
|
||||
// The submit will catch params: `all`(Listeners) and `ev`(Event)
|
||||
return executor().submit(() -> {
|
||||
int count = 0;
|
||||
// Notify all listeners, and ignore the results
|
||||
while (all.hasNext()) {
|
||||
try {
|
||||
all.next().event(ev);
|
||||
count++;
|
||||
} catch (Throwable e) {
|
||||
LOG.warn("Failed to handle event: {}", ev, e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
});
|
||||
}
|
||||
|
||||
public Object call(String event, @Nullable Object... args) {
|
||||
List<EventListener> ls = this.listeners.get(event);
|
||||
if (ls == null) {
|
||||
throw new RuntimeException("Not found listener for: " + event);
|
||||
} else if (ls.size() != 1) {
|
||||
throw new RuntimeException("Too many listeners for: " + event);
|
||||
}
|
||||
EventListener listener = ls.get(0);
|
||||
return listener.event(new Event(this, event, args));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.event;
|
||||
|
||||
public interface EventListener extends java.util.EventListener {
|
||||
/**
|
||||
* The event callback
|
||||
* @param event object
|
||||
* @return event result
|
||||
*/
|
||||
Object event(Event event);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.func;
|
||||
|
||||
public interface TriFunction<T1, T2, T3, R> {
|
||||
|
||||
R apply(T1 v1, T2 v2, T3 v3);
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.iterator;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.InsertionOrderUtil;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class BatchMapperIterator<T, R> extends WrappedIterator<R> {
|
||||
|
||||
private final int batch;
|
||||
private final Iterator<T> originIterator;
|
||||
private final Function<List<T>, Iterator<R>> mapperCallback;
|
||||
|
||||
private Iterator<R> batchIterator;
|
||||
|
||||
public BatchMapperIterator(int batch, Iterator<T> origin,
|
||||
Function<List<T>, Iterator<R>> mapper) {
|
||||
E.checkArgument(batch > 0, "Expect batch > 0, but got %s", batch);
|
||||
this.batch = batch;
|
||||
this.originIterator = origin;
|
||||
this.mapperCallback = mapper;
|
||||
this.batchIterator = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<T> originIterator() {
|
||||
return this.originIterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean fetch() {
|
||||
if (this.batchIterator != null && this.fetchFromBatch()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
List<T> batch = this.nextBatch();
|
||||
assert this.batchIterator == null;
|
||||
while (!batch.isEmpty()) {
|
||||
// Do fetch
|
||||
this.batchIterator = this.mapperCallback.apply(batch);
|
||||
if (this.batchIterator != null && this.fetchFromBatch()) {
|
||||
return true;
|
||||
}
|
||||
// Try next batch
|
||||
batch = this.nextBatch();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected final List<T> nextBatch() {
|
||||
if (!this.originIterator.hasNext()) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
List<T> list = InsertionOrderUtil.newList();
|
||||
for (int i = 0; i < this.batch && this.originIterator.hasNext(); i++) {
|
||||
T next = this.originIterator.next();
|
||||
list.add(next);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
protected final boolean fetchFromBatch() {
|
||||
E.checkNotNull(this.batchIterator, "mapper results");
|
||||
while (this.batchIterator.hasNext()) {
|
||||
R result = this.batchIterator.next();
|
||||
if (result != null) {
|
||||
assert this.current == none();
|
||||
this.current = result;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
this.resetBatchIterator();
|
||||
return false;
|
||||
}
|
||||
|
||||
protected final void resetBatchIterator() {
|
||||
if (this.batchIterator == null) {
|
||||
return;
|
||||
}
|
||||
close(this.batchIterator);
|
||||
this.batchIterator = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.iterator;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public interface CIter<R> extends Iterator<R>, AutoCloseable, Metadatable {
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.iterator;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
|
||||
public class ExtendableIterator<T> extends WrappedIterator<T> {
|
||||
|
||||
private final Deque<Iterator<T>> itors;
|
||||
|
||||
private Iterator<T> currentIterator;
|
||||
|
||||
public ExtendableIterator() {
|
||||
this.itors = new ConcurrentLinkedDeque<>();
|
||||
this.currentIterator = null;
|
||||
}
|
||||
|
||||
public ExtendableIterator(Iterator<T> iter) {
|
||||
this();
|
||||
this.extend(iter);
|
||||
}
|
||||
|
||||
public ExtendableIterator(Iterator<T> itor1, Iterator<T> itor2) {
|
||||
this();
|
||||
this.extend(itor1);
|
||||
this.extend(itor2);
|
||||
}
|
||||
|
||||
public ExtendableIterator<T> extend(Iterator<T> iter) {
|
||||
E.checkState(this.currentIterator == null,
|
||||
"Can't extend iterator after iterating");
|
||||
if (iter != null) {
|
||||
this.itors.addLast(iter);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
for (Iterator<T> iter : this.itors) {
|
||||
if (iter instanceof AutoCloseable) {
|
||||
((AutoCloseable) iter).close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<T> originIterator() {
|
||||
return this.currentIterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean fetch() {
|
||||
assert this.current == none();
|
||||
if (this.itors.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.currentIterator != null && this.currentIterator.hasNext()) {
|
||||
this.current = this.currentIterator.next();
|
||||
return true;
|
||||
}
|
||||
|
||||
Iterator<T> first;
|
||||
while ((first = this.itors.peekFirst()) != null && !first.hasNext()) {
|
||||
if (first == this.itors.peekLast() && this.itors.size() == 1) {
|
||||
this.currentIterator = first;
|
||||
// The last one
|
||||
return false;
|
||||
}
|
||||
close(this.itors.removeFirst());
|
||||
}
|
||||
|
||||
assert first != null && first.hasNext();
|
||||
this.currentIterator = first;
|
||||
this.current = this.currentIterator.next();
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.iterator;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class FilterIterator<T> extends WrappedIterator<T> {
|
||||
|
||||
private final Iterator<T> originIterator;
|
||||
private final Function<T, Boolean> filterCallback;
|
||||
|
||||
public FilterIterator(Iterator<T> origin, Function<T, Boolean> filter) {
|
||||
this.originIterator = origin;
|
||||
this.filterCallback = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<T> originIterator() {
|
||||
return this.originIterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean fetch() {
|
||||
while (this.originIterator.hasNext()) {
|
||||
T next = this.originIterator.next();
|
||||
// Do filter
|
||||
if (next != null && this.filterCallback.apply(next)) {
|
||||
assert this.current == none();
|
||||
this.current = next;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.iterator;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
|
||||
public class FlatMapperFilterIterator<T, R> extends FlatMapperIterator<T, R> {
|
||||
|
||||
private final Function<R, Boolean> filterCallback;
|
||||
|
||||
public FlatMapperFilterIterator(Iterator<T> origin,
|
||||
Function<T, Iterator<R>> mapper,
|
||||
Function<R, Boolean> filter) {
|
||||
super(origin, mapper);
|
||||
this.filterCallback = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean fetchFromBatch() {
|
||||
E.checkNotNull(this.batchIterator, "mapper results");
|
||||
while (this.batchIterator.hasNext()) {
|
||||
R result = this.batchIterator.next();
|
||||
if (result != null && this.filterCallback.apply(result)) {
|
||||
assert this.current == none();
|
||||
this.current = result;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
this.resetBatchIterator();
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.iterator;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
|
||||
public class FlatMapperIterator<T, R> extends WrappedIterator<R> {
|
||||
|
||||
private final Iterator<T> originIterator;
|
||||
private final Function<T, Iterator<R>> mapperCallback;
|
||||
|
||||
protected Iterator<R> batchIterator;
|
||||
|
||||
public FlatMapperIterator(Iterator<T> origin,
|
||||
Function<T, Iterator<R>> mapper) {
|
||||
this.originIterator = origin;
|
||||
this.mapperCallback = mapper;
|
||||
this.batchIterator = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
this.resetBatchIterator();
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<T> originIterator() {
|
||||
return this.originIterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean fetch() {
|
||||
if (this.batchIterator != null && this.fetchFromBatch()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
while (this.originIterator.hasNext()) {
|
||||
T next = this.originIterator.next();
|
||||
assert this.batchIterator == null;
|
||||
// Do fetch
|
||||
this.batchIterator = this.mapperCallback.apply(next);
|
||||
if (this.batchIterator != null && this.fetchFromBatch()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean fetchFromBatch() {
|
||||
E.checkNotNull(this.batchIterator, "mapper results");
|
||||
while (this.batchIterator.hasNext()) {
|
||||
R result = this.batchIterator.next();
|
||||
if (result != null) {
|
||||
assert this.current == none();
|
||||
this.current = result;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
this.resetBatchIterator();
|
||||
return false;
|
||||
}
|
||||
|
||||
protected final void resetBatchIterator() {
|
||||
if (this.batchIterator == null) {
|
||||
return;
|
||||
}
|
||||
close(this.batchIterator);
|
||||
this.batchIterator = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.iterator;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class LimitIterator<T> extends WrappedIterator<T> {
|
||||
|
||||
private final Iterator<T> originIterator;
|
||||
private final Function<T, Boolean> filterCallback;
|
||||
|
||||
public LimitIterator(Iterator<T> origin, Function<T, Boolean> filter) {
|
||||
this.originIterator = origin;
|
||||
this.filterCallback = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<T> originIterator() {
|
||||
return this.originIterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean fetch() {
|
||||
while (this.originIterator.hasNext()) {
|
||||
T next = this.originIterator.next();
|
||||
if (next == null) {
|
||||
continue;
|
||||
}
|
||||
// Do filter
|
||||
boolean reachLimit = this.filterCallback.apply(next);
|
||||
if (reachLimit) {
|
||||
this.closeOriginIterator();
|
||||
return false;
|
||||
}
|
||||
assert this.current == none();
|
||||
this.current = next;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected final void closeOriginIterator() {
|
||||
if (this.originIterator == null) {
|
||||
return;
|
||||
}
|
||||
close(this.originIterator);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.iterator;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hugegraph.util.InsertionOrderUtil;
|
||||
|
||||
public class ListIterator<T> extends WrappedIterator<T> {
|
||||
|
||||
private final Iterator<T> originIterator;
|
||||
private final Iterator<T> resultsIterator;
|
||||
private final Collection<T> results;
|
||||
|
||||
public ListIterator(long capacity, Iterator<T> origin) {
|
||||
List<T> results = InsertionOrderUtil.newList();
|
||||
while (origin.hasNext()) {
|
||||
if (capacity >= 0L && results.size() >= capacity) {
|
||||
throw new IllegalArgumentException(
|
||||
"The iterator exceeded capacity " + capacity);
|
||||
}
|
||||
results.add(origin.next());
|
||||
}
|
||||
this.originIterator = origin;
|
||||
this.results = Collections.unmodifiableList(results);
|
||||
this.resultsIterator = this.results.iterator();
|
||||
}
|
||||
|
||||
public ListIterator(Collection<T> origin) {
|
||||
this.originIterator = origin.iterator();
|
||||
this.results = origin instanceof List ?
|
||||
Collections.unmodifiableList((List<T>) origin) :
|
||||
Collections.unmodifiableCollection(origin);
|
||||
this.resultsIterator = this.results.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
this.resultsIterator.remove();
|
||||
}
|
||||
|
||||
public Collection<T> list() {
|
||||
return this.results;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean fetch() {
|
||||
assert this.current == none();
|
||||
if (!this.resultsIterator.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
this.current = this.resultsIterator.next();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<T> originIterator() {
|
||||
return this.originIterator;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.iterator;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class MapperIterator<T, R> extends WrappedIterator<R> {
|
||||
|
||||
private final Iterator<T> originIterator;
|
||||
private final Function<T, R> mapperCallback;
|
||||
|
||||
public MapperIterator(Iterator<T> origin, Function<T, R> mapper) {
|
||||
this.originIterator = origin;
|
||||
this.mapperCallback = mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<T> originIterator() {
|
||||
return this.originIterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean fetch() {
|
||||
while (this.originIterator.hasNext()) {
|
||||
T next = this.originIterator.next();
|
||||
R result = this.mapperCallback.apply(next);
|
||||
if (result != null) {
|
||||
assert this.current == none();
|
||||
this.current = result;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.iterator;
|
||||
|
||||
public interface Metadatable {
|
||||
|
||||
Object metadata(String meta, Object... args);
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.iterator;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public abstract class WrappedIterator<R> implements CIter<R> {
|
||||
|
||||
private static final Object NONE = new Object();
|
||||
|
||||
protected R current;
|
||||
|
||||
public WrappedIterator() {
|
||||
this.current = none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (this.current != none()) {
|
||||
return true;
|
||||
}
|
||||
return this.fetch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public R next() {
|
||||
if (this.current == none()) {
|
||||
this.fetch();
|
||||
if (this.current == none()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
R current = this.current;
|
||||
this.current = none();
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
Iterator<?> iterator = this.originIterator();
|
||||
if (iterator == null) {
|
||||
throw new NoSuchElementException(
|
||||
"The origin iterator can't be null for removing");
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
Iterator<?> iterator = this.originIterator();
|
||||
if (iterator instanceof AutoCloseable) {
|
||||
((AutoCloseable) iterator).close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object metadata(String meta, Object... args) {
|
||||
Iterator<?> iterator = this.originIterator();
|
||||
if (iterator instanceof Metadatable) {
|
||||
return ((Metadatable) iterator).metadata(meta, args);
|
||||
}
|
||||
throw new IllegalStateException("Original iterator is not Metadatable");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static final <R> R none() {
|
||||
return (R) NONE;
|
||||
}
|
||||
|
||||
public static void close(Iterator<?> iterator) {
|
||||
if (iterator instanceof AutoCloseable) {
|
||||
try {
|
||||
((AutoCloseable) iterator).close();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to close iterator");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Iterator<?> originIterator();
|
||||
|
||||
protected abstract boolean fetch();
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.license;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class LicenseCommonParam {
|
||||
|
||||
@JsonProperty("subject")
|
||||
private String subject;
|
||||
|
||||
@JsonProperty("issued_time")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date issuedTime = new Date();
|
||||
|
||||
@JsonProperty("not_before")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date notBefore = this.issuedTime;
|
||||
|
||||
@JsonProperty("not_after")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date notAfter = DateUtils.addDays(this.notBefore, 30);
|
||||
|
||||
@JsonProperty("consumer_type")
|
||||
private String consumerType = "user";
|
||||
|
||||
@JsonProperty("consumer_amount")
|
||||
private Integer consumerAmount = 1;
|
||||
|
||||
@JsonProperty("description")
|
||||
private String description = "";
|
||||
|
||||
@JsonProperty("extra_params")
|
||||
private List<LicenseExtraParam> extraParams;
|
||||
|
||||
public LicenseCommonParam() {
|
||||
// pass
|
||||
}
|
||||
|
||||
public LicenseCommonParam(String subject, String description,
|
||||
Date issued, Date notBefore, Date notAfter,
|
||||
String consumerType, int consumerAmount,
|
||||
List<LicenseExtraParam> extraParams) {
|
||||
this.subject = subject;
|
||||
this.description = description;
|
||||
this.issuedTime = issued;
|
||||
this.notBefore = notBefore;
|
||||
this.notAfter = notAfter;
|
||||
this.consumerType = consumerType;
|
||||
this.consumerAmount = consumerAmount;
|
||||
this.extraParams = extraParams;
|
||||
}
|
||||
|
||||
public String subject() {
|
||||
return this.subject;
|
||||
}
|
||||
|
||||
public Date issuedTime() {
|
||||
return this.issuedTime;
|
||||
}
|
||||
|
||||
public Date notBefore() {
|
||||
return this.notBefore;
|
||||
}
|
||||
|
||||
public Date notAfter() {
|
||||
return this.notAfter;
|
||||
}
|
||||
|
||||
public String consumerType() {
|
||||
return this.consumerType;
|
||||
}
|
||||
|
||||
public Integer consumerAmount() {
|
||||
return this.consumerAmount;
|
||||
}
|
||||
|
||||
public String description() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public List<LicenseExtraParam> extraParams() {
|
||||
return this.extraParams;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.license;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class LicenseCreateParam extends LicenseCommonParam {
|
||||
|
||||
@JsonProperty("private_alias")
|
||||
private String privateAlias;
|
||||
|
||||
@JsonAlias("key_ticket")
|
||||
@JsonProperty("key_password")
|
||||
private String keyPassword;
|
||||
|
||||
@JsonAlias("store_ticket")
|
||||
@JsonProperty("store_password")
|
||||
private String storePassword;
|
||||
|
||||
@JsonProperty("privatekey_path")
|
||||
private String privateKeyPath;
|
||||
|
||||
@JsonProperty("license_path")
|
||||
private String licensePath;
|
||||
|
||||
public String privateAlias() {
|
||||
return this.privateAlias;
|
||||
}
|
||||
|
||||
public String keyPassword() {
|
||||
return this.keyPassword;
|
||||
}
|
||||
|
||||
public String storePassword() {
|
||||
return this.storePassword;
|
||||
}
|
||||
|
||||
public String privateKeyPath() {
|
||||
return this.privateKeyPath;
|
||||
}
|
||||
|
||||
public String licensePath() {
|
||||
return this.licensePath;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.license;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class LicenseExtraParam {
|
||||
|
||||
public static final int NO_LIMIT = -1;
|
||||
|
||||
@JsonProperty("id")
|
||||
private String id;
|
||||
|
||||
@JsonProperty("version")
|
||||
private String version;
|
||||
|
||||
@JsonProperty("graphs")
|
||||
private int graphs;
|
||||
|
||||
@JsonProperty("ip")
|
||||
private String ip;
|
||||
|
||||
@JsonProperty("mac")
|
||||
private String mac;
|
||||
|
||||
@JsonProperty("cpus")
|
||||
private int cpus;
|
||||
|
||||
// The unit is MB
|
||||
@JsonProperty("ram")
|
||||
private int ram;
|
||||
|
||||
@JsonProperty("threads")
|
||||
private int threads;
|
||||
|
||||
// The unit is MB
|
||||
@JsonProperty("memory")
|
||||
private int memory;
|
||||
|
||||
@JsonProperty("nodes")
|
||||
private int nodes;
|
||||
|
||||
// The unit is MB
|
||||
@JsonProperty("data_size")
|
||||
private long dataSize;
|
||||
|
||||
@JsonProperty("vertices")
|
||||
private long vertices;
|
||||
|
||||
@JsonProperty("edges")
|
||||
private long edges;
|
||||
|
||||
public String id() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String version() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public int graphs() {
|
||||
return this.graphs;
|
||||
}
|
||||
|
||||
public String ip() {
|
||||
return this.ip;
|
||||
}
|
||||
|
||||
public String mac() {
|
||||
return this.mac;
|
||||
}
|
||||
|
||||
public int cpus() {
|
||||
return this.cpus;
|
||||
}
|
||||
|
||||
public int ram() {
|
||||
return this.ram;
|
||||
}
|
||||
|
||||
public int threads() {
|
||||
return this.threads;
|
||||
}
|
||||
|
||||
public int memory() {
|
||||
return this.memory;
|
||||
}
|
||||
|
||||
public int nodes() {
|
||||
return this.nodes;
|
||||
}
|
||||
|
||||
public long dataSize() {
|
||||
return this.dataSize;
|
||||
}
|
||||
|
||||
public long vertices() {
|
||||
return this.vertices;
|
||||
}
|
||||
|
||||
public long edges() {
|
||||
return this.edges;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.license;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class LicenseInstallParam {
|
||||
|
||||
@JsonProperty("subject")
|
||||
private String subject;
|
||||
|
||||
@JsonProperty("public_alias")
|
||||
private String publicAlias;
|
||||
|
||||
@JsonAlias("store_ticket")
|
||||
@JsonProperty("store_password")
|
||||
private String storePassword;
|
||||
|
||||
@JsonProperty("publickey_path")
|
||||
private String publicKeyPath;
|
||||
|
||||
@JsonProperty("license_path")
|
||||
private String licensePath;
|
||||
|
||||
public String subject() {
|
||||
return this.subject;
|
||||
}
|
||||
|
||||
public String publicAlias() {
|
||||
return this.publicAlias;
|
||||
}
|
||||
|
||||
public String storePassword() {
|
||||
return this.storePassword;
|
||||
}
|
||||
|
||||
public String licensePath() {
|
||||
return this.licensePath;
|
||||
}
|
||||
|
||||
public String publicKeyPath() {
|
||||
return this.publicKeyPath;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.license;
|
||||
|
||||
public interface LicenseManager {
|
||||
|
||||
LicenseParams installLicense() throws Exception;
|
||||
|
||||
void uninstallLicense() throws Exception;
|
||||
|
||||
LicenseParams verifyLicense() throws Exception;
|
||||
|
||||
interface VerifyCallback {
|
||||
|
||||
void onVerifyLicense(LicenseParams params) throws Exception;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.license;
|
||||
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
|
||||
import org.apache.hugegraph.license.LicenseManager.VerifyCallback;
|
||||
|
||||
public class LicenseManagerFactory {
|
||||
|
||||
public static LicenseManager create(LicenseInstallParam param,
|
||||
VerifyCallback veryfyCallback) {
|
||||
throw new NotImplementedException("No LicenseManager available");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.license;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class LicenseParams extends LicenseCommonParam {
|
||||
|
||||
public LicenseParams() {
|
||||
super();
|
||||
}
|
||||
|
||||
public LicenseParams(String subject, String description,
|
||||
Date issued, Date notBefore, Date notAfter,
|
||||
String consumerType, int consumerAmount,
|
||||
List<LicenseExtraParam> extraParams) {
|
||||
super(subject, description, issued, notBefore, notAfter,
|
||||
consumerType, consumerAmount, extraParams);
|
||||
}
|
||||
|
||||
public LicenseExtraParam matchParam(String id) {
|
||||
for (LicenseExtraParam param : this.extraParams()) {
|
||||
if (param.id().equals(id)) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.license;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MachineInfo {
|
||||
|
||||
private List<String> ipAddressList;
|
||||
private List<String> macAddressList;
|
||||
|
||||
public MachineInfo() {
|
||||
this.ipAddressList = null;
|
||||
this.macAddressList = null;
|
||||
}
|
||||
|
||||
public List<String> getIpAddress() {
|
||||
if (this.ipAddressList != null) {
|
||||
return this.ipAddressList;
|
||||
}
|
||||
this.ipAddressList = new ArrayList<>();
|
||||
List<InetAddress> inetAddresses = this.getLocalAllInetAddress();
|
||||
if (inetAddresses != null && !inetAddresses.isEmpty()) {
|
||||
this.ipAddressList = inetAddresses.stream()
|
||||
.map(InetAddress::getHostAddress)
|
||||
.distinct()
|
||||
.map(String::toLowerCase)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return this.ipAddressList;
|
||||
}
|
||||
|
||||
public List<String> getMacAddress() {
|
||||
if (this.macAddressList != null) {
|
||||
return this.macAddressList;
|
||||
}
|
||||
this.macAddressList = new ArrayList<>();
|
||||
List<InetAddress> inetAddresses = this.getLocalAllInetAddress();
|
||||
if (inetAddresses != null && !inetAddresses.isEmpty()) {
|
||||
// Get the Mac address of all network interfaces
|
||||
List<String> list = new ArrayList<>();
|
||||
Set<String> uniqueValues = new HashSet<>();
|
||||
for (InetAddress inetAddress : inetAddresses) {
|
||||
String macByInetAddress = this.getMacByInetAddress(inetAddress);
|
||||
if (uniqueValues.add(macByInetAddress)) {
|
||||
list.add(macByInetAddress);
|
||||
}
|
||||
}
|
||||
this.macAddressList = list;
|
||||
}
|
||||
return this.macAddressList;
|
||||
}
|
||||
|
||||
public List<InetAddress> getLocalAllInetAddress() {
|
||||
Enumeration<NetworkInterface> interfaces;
|
||||
try {
|
||||
interfaces = NetworkInterface.getNetworkInterfaces();
|
||||
} catch (SocketException e) {
|
||||
throw new RuntimeException("Failed to get network interfaces");
|
||||
}
|
||||
|
||||
List<InetAddress> result = new ArrayList<>();
|
||||
while (interfaces.hasMoreElements()) {
|
||||
NetworkInterface nw = interfaces.nextElement();
|
||||
for (Enumeration<InetAddress> inetAddresses = nw.getInetAddresses();
|
||||
inetAddresses.hasMoreElements(); ) {
|
||||
InetAddress inetAddr = inetAddresses.nextElement();
|
||||
if (!inetAddr.isLoopbackAddress() &&
|
||||
!inetAddr.isLinkLocalAddress() &&
|
||||
!inetAddr.isMulticastAddress()) {
|
||||
result.add(inetAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getMacByInetAddress(InetAddress inetAddr) {
|
||||
byte[] mac;
|
||||
try {
|
||||
mac = NetworkInterface.getByInetAddress(inetAddr)
|
||||
.getHardwareAddress();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Failed to get mac address for inet address '%s'",
|
||||
inetAddr));
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < mac.length; i++) {
|
||||
if (i != 0) {
|
||||
sb.append("-");
|
||||
}
|
||||
String temp = Integer.toHexString(mac[i] & 0xff);
|
||||
if (temp.length() == 1) {
|
||||
sb.append("0").append(temp);
|
||||
} else {
|
||||
sb.append(temp);
|
||||
}
|
||||
}
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.perf;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hugegraph.perf.PerfUtil.FastMap;
|
||||
|
||||
public final class LightStopwatch implements Stopwatch {
|
||||
|
||||
private long lastStartTime = -1L;
|
||||
|
||||
private long times = 0L;
|
||||
private long totalCost = 0L;
|
||||
private long totalChildrenTimes = -1L;
|
||||
|
||||
private final String name;
|
||||
private final Path parent;
|
||||
private final Path id;
|
||||
private final FastMap<String, Stopwatch> children;
|
||||
|
||||
public LightStopwatch(String name, Stopwatch parent) {
|
||||
this(name, parent.id());
|
||||
parent.child(name, this);
|
||||
}
|
||||
|
||||
public LightStopwatch(String name, Path parent) {
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
this.id = Stopwatch.id(parent, name);
|
||||
this.children = new FastMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path id() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path parent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastStartTime(long startTime) {
|
||||
this.lastStartTime = startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTime(long startTime) {
|
||||
this.times++;
|
||||
this.lastStartTime = startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTime(long startTime) {
|
||||
this.totalCost += PerfUtil.now() - this.lastStartTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long times() {
|
||||
return this.times;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalTimes() {
|
||||
if (this.totalChildrenTimes > 0L) {
|
||||
return this.times + this.totalChildrenTimes;
|
||||
}
|
||||
return this.times;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalChildrenTimes() {
|
||||
return this.totalChildrenTimes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalCost() {
|
||||
return this.totalCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void totalCost(long totalCost) {
|
||||
this.totalCost = totalCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long minCost() {
|
||||
return -1L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long maxCost() {
|
||||
return -1L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalWasted() {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalSelfWasted() {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalChildrenWasted() {
|
||||
return -1L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillChildrenTotal(List<Stopwatch> children) {
|
||||
// Fill total times of children
|
||||
this.totalChildrenTimes = children.stream().mapToLong(Stopwatch::totalTimes).sum();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LightStopwatch copy() {
|
||||
try {
|
||||
return (LightStopwatch) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stopwatch child(String name) {
|
||||
return this.children.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stopwatch child(String name, Stopwatch watch) {
|
||||
if (watch == null) {
|
||||
return this.children.remove(name);
|
||||
}
|
||||
return this.children.put(name, watch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean empty() {
|
||||
return this.children.size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.lastStartTime = -1L;
|
||||
|
||||
this.times = 0L;
|
||||
this.totalCost = 0L;
|
||||
this.totalChildrenTimes = -1L;
|
||||
|
||||
this.children.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{parent:%s,name:%s," +
|
||||
"times:%s,totalChildrenTimes:%s,totalCost:%s}",
|
||||
this.parent, this.name,
|
||||
this.times, this.totalChildrenTimes,
|
||||
this.totalCost);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.perf;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.testutil.Whitebox;
|
||||
|
||||
public final class NormalStopwatch implements Stopwatch {
|
||||
|
||||
private static final Logger LOG = Log.logger(Stopwatch.class);
|
||||
|
||||
private static final String MULTI_THREAD_ACCESS_ERROR =
|
||||
"There may be multi-threaded access, ensure " +
|
||||
"not call PerfUtil.profileSingleThread(true) when " +
|
||||
"multithreading.";
|
||||
|
||||
private long lastStartTime = -1L;
|
||||
|
||||
private long times = 0L;
|
||||
private long totalCost = 0L;
|
||||
private long minCost = Long.MAX_VALUE;
|
||||
private long maxCost = 0L;
|
||||
private long totalSelfWasted = 0L;
|
||||
private long totalChildrenWasted = -1L;
|
||||
private long totalChildrenTimes = -1L;
|
||||
|
||||
private final String name;
|
||||
private final Path parent;
|
||||
private final Path id;
|
||||
private final PerfUtil.FastMap<String, Stopwatch> children;
|
||||
|
||||
public NormalStopwatch(String name, Stopwatch parent) {
|
||||
this(name, parent.id());
|
||||
parent.child(name, this);
|
||||
}
|
||||
|
||||
public NormalStopwatch(String name, Path parent) {
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
this.id = Stopwatch.id(parent, name);
|
||||
this.children = new PerfUtil.FastMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path id() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path parent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastStartTime(long startTime) {
|
||||
this.lastStartTime = startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTime(long startTime) {
|
||||
assert this.lastStartTime == -1L : MULTI_THREAD_ACCESS_ERROR;
|
||||
|
||||
this.times++;
|
||||
this.lastStartTime = startTime;
|
||||
|
||||
long endTime = PerfUtil.now();
|
||||
long wastedTime = endTime - startTime;
|
||||
if (wastedTime <= 0L) {
|
||||
wastedTime += eachStartWastedLost;
|
||||
}
|
||||
|
||||
this.totalSelfWasted += wastedTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTime(long startTime) {
|
||||
assert startTime >= this.lastStartTime && this.lastStartTime != -1L :
|
||||
MULTI_THREAD_ACCESS_ERROR;
|
||||
|
||||
long endTime = PerfUtil.now();
|
||||
// The following code cost about 3ns~4ns
|
||||
long wastedTime = endTime - startTime;
|
||||
if (wastedTime <= 0L) {
|
||||
wastedTime += eachEndWastedLost;
|
||||
}
|
||||
|
||||
long cost = endTime - this.lastStartTime;
|
||||
|
||||
if (this.minCost > cost) {
|
||||
this.minCost = cost;
|
||||
}
|
||||
if (this.maxCost < cost) {
|
||||
this.maxCost = cost;
|
||||
}
|
||||
|
||||
this.totalCost += cost;
|
||||
this.totalSelfWasted += wastedTime;
|
||||
this.lastStartTime = -1L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long times() {
|
||||
return this.times;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalCost() {
|
||||
return this.totalCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void totalCost(long totalCost) {
|
||||
this.totalCost = totalCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long minCost() {
|
||||
return this.minCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long maxCost() {
|
||||
return this.maxCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalTimes() {
|
||||
if (this.totalChildrenTimes > 0L) {
|
||||
return this.times + this.totalChildrenTimes;
|
||||
}
|
||||
return this.times;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalChildrenTimes() {
|
||||
return this.totalChildrenTimes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalWasted() {
|
||||
if (this.totalChildrenWasted > 0L) {
|
||||
return this.totalSelfWasted + this.totalChildrenWasted;
|
||||
}
|
||||
return this.totalSelfWasted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalSelfWasted() {
|
||||
return this.totalSelfWasted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalChildrenWasted() {
|
||||
return this.totalChildrenWasted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillChildrenTotal(List<Stopwatch> children) {
|
||||
// Fill total wasted cost of children
|
||||
this.totalChildrenWasted = children.stream().mapToLong(Stopwatch::totalWasted).sum();
|
||||
// Fill total times of children
|
||||
this.totalChildrenTimes = children.stream().mapToLong(Stopwatch::totalTimes).sum();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stopwatch copy() {
|
||||
try {
|
||||
return (Stopwatch) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stopwatch child(String name) {
|
||||
return this.children.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stopwatch child(String name, Stopwatch watch) {
|
||||
if (watch == null) {
|
||||
return this.children.remove(name);
|
||||
}
|
||||
return this.children.put(name, watch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean empty() {
|
||||
return this.children.size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.lastStartTime = -1L;
|
||||
|
||||
this.times = 0L;
|
||||
this.totalCost = 0L;
|
||||
|
||||
this.minCost = Long.MAX_VALUE;
|
||||
this.maxCost = 0L;
|
||||
this.totalSelfWasted = 0L;
|
||||
this.totalChildrenWasted = -1L;
|
||||
this.totalChildrenTimes = -1L;
|
||||
|
||||
this.children.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{parent:%s,name:%s," +
|
||||
"times:%s,totalChildrenTimes:%s," +
|
||||
"totalCost:%s,minCost:%s,maxCost:%s," +
|
||||
"totalSelfWasted:%s,totalChildrenWasted:%s}",
|
||||
this.parent, this.name,
|
||||
this.times, this.totalChildrenTimes,
|
||||
this.totalCost, this.minCost, this.maxCost,
|
||||
this.totalSelfWasted, this.totalChildrenWasted);
|
||||
}
|
||||
|
||||
private static long eachStartWastedLost = 0L;
|
||||
private static long eachEndWastedLost = 0L;
|
||||
|
||||
protected static void initEachWastedLost() {
|
||||
int times = 100000000;
|
||||
|
||||
PerfUtil.LocalStack<Stopwatch> callStack = Whitebox.getInternalState(
|
||||
PerfUtil.instance(), "callStack");
|
||||
|
||||
long baseStart = PerfUtil.now();
|
||||
for (int i = 0; i < times; i++) {
|
||||
PerfUtil.instance();
|
||||
}
|
||||
long baseCost = PerfUtil.now() - baseStart;
|
||||
|
||||
BiFunction<String, Runnable, Long> testEachCost = (name, test) -> {
|
||||
long start = PerfUtil.now();
|
||||
test.run();
|
||||
long end = PerfUtil.now();
|
||||
long cost = end - start - baseCost;
|
||||
if (cost < 0L) {
|
||||
cost = 0L;
|
||||
}
|
||||
long eachCost = cost / times;
|
||||
|
||||
LOG.info("Wasted time test: cost={}ms, base_cost={}ms, {}={}ns",
|
||||
cost / 1000000.0, baseCost / 1000000.0, name, eachCost);
|
||||
return eachCost;
|
||||
};
|
||||
|
||||
String startName = "each_start_cost";
|
||||
eachStartWastedLost = testEachCost.apply(startName, () -> {
|
||||
Stopwatch watch = PerfUtil.instance().start(startName);
|
||||
PerfUtil.instance().end(startName);
|
||||
for (int i = 0; i < times; i++) {
|
||||
// Test call start()
|
||||
PerfUtil.instance().start(startName);
|
||||
// Mock end()
|
||||
watch.lastStartTime(-1L);
|
||||
callStack.pop();
|
||||
}
|
||||
});
|
||||
|
||||
String endName = "each_end_cost";
|
||||
eachEndWastedLost = testEachCost.apply(endName, () -> {
|
||||
Stopwatch watch = PerfUtil.instance().start(endName);
|
||||
PerfUtil.instance().end(endName);
|
||||
for (int i = 0; i < times; i++) {
|
||||
// Mock start()
|
||||
callStack.push(watch);
|
||||
watch.lastStartTime(0L);
|
||||
// Test call start()
|
||||
PerfUtil.instance().end(endName);
|
||||
watch.totalCost(0L);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,687 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.perf;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.EmptyStackException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.hugegraph.util.ReflectionUtil;
|
||||
import org.apache.hugegraph.func.TriFunction;
|
||||
import org.apache.hugegraph.testutil.Assert.ThrowableConsumer;
|
||||
import org.apache.hugegraph.perf.Stopwatch.Path;
|
||||
import com.google.common.reflect.ClassPath.ClassInfo;
|
||||
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtMethod;
|
||||
|
||||
public final class PerfUtil {
|
||||
|
||||
private static final Logger LOG = Log.logger(PerfUtil.class);
|
||||
private static final int DEFAULT_CAPACITY = 1024;
|
||||
|
||||
private static final ThreadLocal<PerfUtil> INSTANCE = new ThreadLocal<>();
|
||||
|
||||
private static PerfUtil SINGLE_INSTANCE = null;
|
||||
private static Thread SINGLE_THREAD = null;
|
||||
private static LocalTimer LOCAL_TIMER = null;
|
||||
private static boolean LIGHT_WATCH = false;
|
||||
|
||||
private final Map<Path, Stopwatch> stopwatches;
|
||||
private final LocalStack<Stopwatch> callStack;
|
||||
private final Stopwatch root;
|
||||
|
||||
private PerfUtil() {
|
||||
this.stopwatches = new HashMap<>(DEFAULT_CAPACITY);
|
||||
this.callStack = new LocalStack<>(DEFAULT_CAPACITY);
|
||||
this.root = newStopwatch(Path.ROOT_NAME, Path.EMPTY);
|
||||
}
|
||||
|
||||
public static PerfUtil instance() {
|
||||
if (SINGLE_INSTANCE != null &&
|
||||
SINGLE_THREAD == Thread.currentThread()) {
|
||||
// Return the only one instance for single thread, for performance
|
||||
return SINGLE_INSTANCE;
|
||||
}
|
||||
|
||||
PerfUtil p = INSTANCE.get();
|
||||
if (p == null) {
|
||||
p = new PerfUtil();
|
||||
INSTANCE.set(p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public static void profileSingleThread(boolean yes) {
|
||||
SINGLE_INSTANCE = yes ? PerfUtil.instance() : null;
|
||||
SINGLE_THREAD = yes ? Thread.currentThread() : null;
|
||||
}
|
||||
|
||||
public static void useLocalTimer(boolean yes) {
|
||||
if (yes) {
|
||||
if (LOCAL_TIMER != null) {
|
||||
return;
|
||||
}
|
||||
LOCAL_TIMER = new LocalTimer();
|
||||
try {
|
||||
LOCAL_TIMER.startTimeUpdateLoop();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (!LIGHT_WATCH) {
|
||||
NormalStopwatch.initEachWastedLost();
|
||||
}
|
||||
} else {
|
||||
if (LOCAL_TIMER == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
LOCAL_TIMER.stop();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
LOCAL_TIMER = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void useLightStopwatch(boolean yes) {
|
||||
if (yes != LIGHT_WATCH) {
|
||||
PerfUtil instance = INSTANCE.get();
|
||||
boolean empty = instance == null || instance.empty();
|
||||
String message = "Please call clear() before switching " +
|
||||
"light-stopwatch due to there is dirty watch";
|
||||
E.checkArgument(empty, message);
|
||||
}
|
||||
LIGHT_WATCH = yes;
|
||||
}
|
||||
|
||||
protected static long now() {
|
||||
if (LOCAL_TIMER != null) {
|
||||
return LOCAL_TIMER.now();
|
||||
}
|
||||
// System.nanoTime() cost about 40 ns each call
|
||||
return System.nanoTime();
|
||||
}
|
||||
|
||||
protected static Stopwatch newStopwatch(String name, Path parent) {
|
||||
return LIGHT_WATCH ? new LightStopwatch(name, parent) :
|
||||
new NormalStopwatch(name, parent);
|
||||
}
|
||||
|
||||
protected static Stopwatch newStopwatch(String name, Stopwatch parent) {
|
||||
return LIGHT_WATCH ? new LightStopwatch(name, parent) :
|
||||
new NormalStopwatch(name, parent);
|
||||
}
|
||||
|
||||
public Stopwatch start(String name) {
|
||||
long start = now();
|
||||
|
||||
Stopwatch parent = this.callStack.empty() ?
|
||||
this.root : this.callStack.peek();
|
||||
|
||||
// Get watch by name from local tree
|
||||
Stopwatch watch = parent.child(name);
|
||||
if (watch == null) {
|
||||
watch = newStopwatch(name, parent);
|
||||
assert !this.stopwatches.containsKey(watch.id()) : watch;
|
||||
this.stopwatches.put(watch.id(), watch);
|
||||
}
|
||||
this.callStack.push(watch);
|
||||
|
||||
watch.startTime(start);
|
||||
|
||||
return watch;
|
||||
}
|
||||
|
||||
public Stopwatch start2(String name) {
|
||||
long start = now(); // cost 70 ns with System.nanoTime()
|
||||
|
||||
Path parent = this.callStack.empty() ?
|
||||
Path.EMPTY : this.callStack.peek().id();
|
||||
Path id = Stopwatch.id(parent, name); // cost 130
|
||||
// Get watch by id from global map
|
||||
Stopwatch watch = this.stopwatches.get(id); // cost 170
|
||||
if (watch == null) {
|
||||
watch = newStopwatch(name, parent);
|
||||
this.stopwatches.put(watch.id(), watch); // cost 180
|
||||
}
|
||||
this.callStack.push(watch); // cost 190
|
||||
|
||||
watch.startTime(start);
|
||||
|
||||
return watch;
|
||||
}
|
||||
|
||||
public void end(String name) {
|
||||
long start = LIGHT_WATCH ? 0L : now();
|
||||
|
||||
Stopwatch watch = this.callStack.pop();
|
||||
if (watch == null || watch.name() != name) {
|
||||
throw new IllegalArgumentException("Invalid watch name: " + name);
|
||||
}
|
||||
|
||||
watch.endTime(start);
|
||||
}
|
||||
|
||||
public boolean empty() {
|
||||
return this.stopwatches.isEmpty() && this.root.empty();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
String error = "Can't be cleared when the call has not ended yet";
|
||||
E.checkState(this.callStack.empty(), error);
|
||||
|
||||
this.stopwatches.clear();
|
||||
this.root.clear();
|
||||
}
|
||||
|
||||
public void profilePackage(String... packages) throws Throwable {
|
||||
Set<String> loadedClasses = new HashSet<>();
|
||||
|
||||
Function<String, Boolean> inPackage = (cls) -> {
|
||||
for (String pkg : packages) {
|
||||
if (cls.startsWith(pkg)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
ThrowableConsumer<String> profileClassIfPresent = (cls) -> {
|
||||
if (!loadedClasses.contains(cls)) {
|
||||
// Profile super class
|
||||
for (String s : ReflectionUtil.superClasses(cls)) {
|
||||
if (!loadedClasses.contains(s) && inPackage.apply(s)) {
|
||||
profileClass(s);
|
||||
loadedClasses.add(s);
|
||||
}
|
||||
}
|
||||
// Profile self class
|
||||
profileClass(cls);
|
||||
loadedClasses.add(cls);
|
||||
}
|
||||
};
|
||||
|
||||
Iterator<ClassInfo> classes = ReflectionUtil.classes(packages);
|
||||
while (classes.hasNext()) {
|
||||
String cls = classes.next().getName();
|
||||
// Profile self class
|
||||
profileClassIfPresent.accept(cls);
|
||||
// Profile nested class
|
||||
for (String s : ReflectionUtil.nestedClasses(cls)) {
|
||||
profileClassIfPresent.accept(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void profileClass(String... classes) throws Throwable {
|
||||
ClassPool classPool = ClassPool.getDefault();
|
||||
|
||||
for (String cls : classes) {
|
||||
CtClass ctClass = classPool.get(cls);
|
||||
List<CtMethod> methods = ReflectionUtil.getMethodsAnnotatedWith(
|
||||
ctClass, Watched.class, false);
|
||||
for (CtMethod method : methods) {
|
||||
profile(method);
|
||||
}
|
||||
|
||||
// Load class and make it effective
|
||||
if (!methods.isEmpty()) {
|
||||
ctClass.toClass();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void profile(CtMethod ctMethod)
|
||||
throws CannotCompileException, ClassNotFoundException {
|
||||
final String START =
|
||||
"org.apache.hugegraph.perf.PerfUtil.instance().start(\"%s\");";
|
||||
final String END =
|
||||
"org.apache.hugegraph.perf.PerfUtil.instance().end(\"%s\");";
|
||||
|
||||
Watched annotation = (Watched) ctMethod.getAnnotation(Watched.class);
|
||||
|
||||
String name = annotation.value();
|
||||
if (name.isEmpty()) {
|
||||
name = ctMethod.getName();
|
||||
}
|
||||
if (!annotation.prefix().isEmpty()) {
|
||||
name = annotation.prefix() + "." + name;
|
||||
}
|
||||
|
||||
ctMethod.insertBefore(String.format(START, name));
|
||||
// Insert as a finally-statement
|
||||
ctMethod.insertAfter(String.format(END, name), true);
|
||||
|
||||
LOG.debug("Profiled for: '{}' [{}]", name, ctMethod.getLongName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.stopwatches.toString();
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
StringBuilder sb = new StringBuilder(8 + this.stopwatches.size() * 96);
|
||||
sb.append('{');
|
||||
for (Map.Entry<Path, Stopwatch> w : this.stopwatches.entrySet()) {
|
||||
sb.append('"');
|
||||
sb.append(w.getKey());
|
||||
sb.append('"');
|
||||
|
||||
sb.append(':');
|
||||
|
||||
sb.append(w.getValue().toJson());
|
||||
|
||||
sb.append(',');
|
||||
}
|
||||
if (!this.stopwatches.isEmpty()) {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// TODO: move toECharts() method out of this class
|
||||
public String toECharts() {
|
||||
TriFunction<Integer, Integer, List<Stopwatch>, String> formatLevel = (
|
||||
totalDepth, depth, items) -> {
|
||||
float factor = 100.0f / (totalDepth + 1);
|
||||
float showFactor = 1 + (totalDepth - depth) / (float) depth;
|
||||
|
||||
float radiusFrom = depth * factor;
|
||||
float radiusTo = depth * factor + factor;
|
||||
if (depth == 1) {
|
||||
radiusFrom = 0;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(8 + items.size() * 128);
|
||||
sb.append('{');
|
||||
sb.append("name: 'Total Cost',");
|
||||
sb.append("type: 'pie',");
|
||||
sb.append(String.format("radius: ['%s%%', '%s%%'],",
|
||||
radiusFrom, radiusTo));
|
||||
sb.append(String.format(
|
||||
"label: {normal: {position: 'inner', formatter:" +
|
||||
"function(params) {" +
|
||||
" if (params.percent > %s) return params.data.name;" +
|
||||
" else return '';" +
|
||||
"}}},", showFactor));
|
||||
sb.append("data: [");
|
||||
|
||||
items.sort((i, j) -> i.id().compareTo(j.id()));
|
||||
for (Stopwatch w : items) {
|
||||
sb.append('{');
|
||||
|
||||
sb.append("id:'");
|
||||
sb.append(w.id());
|
||||
sb.append("',");
|
||||
|
||||
sb.append("name:'");
|
||||
sb.append(w.name());
|
||||
sb.append("',");
|
||||
|
||||
sb.append("value:");
|
||||
// w.totalCost() - w.totalWasted() ?
|
||||
sb.append(w.totalCost());
|
||||
sb.append(',');
|
||||
|
||||
sb.append("cost:");
|
||||
sb.append(w.totalCost() / 1000000.0);
|
||||
sb.append(',');
|
||||
|
||||
sb.append("minCost:");
|
||||
sb.append(w.minCost());
|
||||
sb.append(',');
|
||||
|
||||
sb.append("maxCost:");
|
||||
sb.append(w.maxCost());
|
||||
sb.append(',');
|
||||
|
||||
sb.append("wasted:");
|
||||
sb.append(w.totalWasted() / 1000000.0);
|
||||
sb.append(',');
|
||||
|
||||
sb.append("selfWasted:");
|
||||
sb.append(w.totalSelfWasted() / 1000000.0);
|
||||
sb.append(',');
|
||||
|
||||
sb.append("times:");
|
||||
sb.append(w.times());
|
||||
sb.append(',');
|
||||
|
||||
sb.append("totalTimes:");
|
||||
sb.append(w.totalTimes());
|
||||
|
||||
sb.append('}');
|
||||
sb.append(',');
|
||||
}
|
||||
if (!items.isEmpty()) {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
sb.append("]}");
|
||||
return sb.toString();
|
||||
};
|
||||
|
||||
BiConsumer<List<Stopwatch>, List<Stopwatch>> fillChildrenTotal =
|
||||
(itemsOfLn, itemsOfLnParent) -> {
|
||||
for (Stopwatch parent : itemsOfLnParent) {
|
||||
List<Stopwatch> children = itemsOfLn.stream().filter(c -> {
|
||||
return c.parent().equals(parent.id());
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
parent.fillChildrenTotal(children);
|
||||
}
|
||||
};
|
||||
|
||||
BiConsumer<List<Stopwatch>, List<Stopwatch>> fillOther =
|
||||
(itemsOfLn, itemsOfLnParent) -> {
|
||||
for (Stopwatch parent : itemsOfLnParent) {
|
||||
Stream<Stopwatch> children = itemsOfLn.stream().filter(c -> {
|
||||
return c.parent().equals(parent.id());
|
||||
});
|
||||
// Fill other cost
|
||||
long sumCost = children.mapToLong(Stopwatch::totalCost).sum();
|
||||
long otherCost = parent.totalCost() - sumCost;
|
||||
if (otherCost > 0L) {
|
||||
Stopwatch other = newStopwatch("~", parent.id());
|
||||
other.totalCost(otherCost);
|
||||
itemsOfLn.add(other);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Map<Path, Stopwatch> items = this.stopwatches;
|
||||
Map<Integer, List<Stopwatch>> levelItems = new HashMap<>();
|
||||
int maxDepth = 0;
|
||||
for (Map.Entry<Path, Stopwatch> e : items.entrySet()) {
|
||||
int depth = e.getKey().toString().split("/").length;
|
||||
List<Stopwatch> levelItem = levelItems.get(depth);
|
||||
if (levelItem == null) {
|
||||
levelItem = new LinkedList<>();
|
||||
levelItems.putIfAbsent(depth, levelItem);
|
||||
}
|
||||
levelItem.add(e.getValue().copy());
|
||||
if (depth > maxDepth) {
|
||||
maxDepth = depth;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill wasted cost from the outermost to innermost
|
||||
for (int i = maxDepth; i > 0; i--) {
|
||||
assert levelItems.containsKey(i) : i;
|
||||
List<Stopwatch> itemsOfI = levelItems.get(i);
|
||||
List<Stopwatch> itemsOfParent = levelItems.get(i - 1);
|
||||
if (itemsOfParent != null) {
|
||||
// Fill total value of children
|
||||
fillChildrenTotal.accept(itemsOfI, itemsOfParent);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(8 + items.size() * 128);
|
||||
// Output results header
|
||||
sb.append("{");
|
||||
sb.append("tooltip: {trigger: 'item', " +
|
||||
"formatter: function(params) {" +
|
||||
" return params.data.name + ' ' + params.percent + '% <br/>'" +
|
||||
" + 'cost: ' + params.data.cost + ' (ms) <br/>'" +
|
||||
" + 'min cost: ' + params.data.minCost + ' (ns) <br/>'" +
|
||||
" + 'max cost: ' + params.data.maxCost + ' (ns) <br/>'" +
|
||||
" + 'wasted: ' + params.data.wasted + ' (ms) <br/>'" +
|
||||
" + 'self wasted: ' + params.data.selfWasted + ' (ms) <br/>'" +
|
||||
" + 'times: ' + params.data.times + '<br/>'" +
|
||||
" + 'total times: ' + params.data.totalTimes + '<br/>'" +
|
||||
" + 'path: ' + params.data.id + '<br/>';" +
|
||||
"}");
|
||||
sb.append("},");
|
||||
sb.append("series: [");
|
||||
// Output results data
|
||||
for (int i = 1; i <= maxDepth; i++) {
|
||||
assert levelItems.containsKey(i) : i;
|
||||
List<Stopwatch> itemsOfI = levelItems.get(i);
|
||||
List<Stopwatch> itemsOfParent = levelItems.get(i - 1);
|
||||
if (itemsOfParent != null) {
|
||||
// Fill other cost for non-root level, ignore root level (i=1)
|
||||
fillOther.accept(itemsOfI, itemsOfParent);
|
||||
}
|
||||
// Output items of level I
|
||||
sb.append(formatLevel.apply(maxDepth, i, itemsOfI));
|
||||
sb.append(',');
|
||||
}
|
||||
if (!items.isEmpty()) {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
sb.append("]}");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static final class LocalTimer {
|
||||
|
||||
// Header: 4 bytes classptr + 8 bytes markword
|
||||
private volatile long padding11 = 0L;
|
||||
private volatile long padding12 = 0L;
|
||||
private volatile long padding13 = 0L;
|
||||
private volatile long padding14 = 0L;
|
||||
private volatile long padding15 = 0L;
|
||||
private volatile long padding16 = 0L; // the 1st 64 bytes
|
||||
|
||||
private volatile long time = 0L;
|
||||
|
||||
private volatile long padding21 = 0L;
|
||||
private volatile long padding22 = 0L;
|
||||
private volatile long padding23 = 0L;
|
||||
private volatile long padding24 = 0L;
|
||||
private volatile long padding25 = 0L;
|
||||
private volatile long padding26 = 0L;
|
||||
private volatile long padding27 = 0L; // the 2nd 64 bytes
|
||||
|
||||
private volatile boolean running = false;
|
||||
private Thread thread = null;
|
||||
|
||||
public long now() {
|
||||
// Read current ns time (be called frequently)
|
||||
return this.time;
|
||||
}
|
||||
|
||||
public void startTimeUpdateLoop() throws InterruptedException {
|
||||
assert this.thread == null;
|
||||
assert this.preventOptimizePadding() == 0L;
|
||||
this.running = true;
|
||||
CountDownLatch started = new CountDownLatch(1);
|
||||
this.thread = new Thread(() -> {
|
||||
started.countDown();
|
||||
while (this.running) {
|
||||
this.time = System.nanoTime();
|
||||
// Prevent frequent updates for perf (5.2s => 3.6s for 8kw)
|
||||
Thread.yield();
|
||||
}
|
||||
}, "LocalTimer");
|
||||
this.thread.setDaemon(true);
|
||||
this.thread.start();
|
||||
started.await();
|
||||
}
|
||||
|
||||
public void stop() throws InterruptedException {
|
||||
this.running = false;
|
||||
if (this.thread != null) {
|
||||
this.thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
public long preventOptimizePadding() {
|
||||
long p1 = this.padding11 + this.padding12 + this.padding13 +
|
||||
this.padding14 + this.padding15 + this.padding16;
|
||||
long p2 = this.padding21 + this.padding22 + this.padding23 +
|
||||
this.padding24 + this.padding25 + this.padding26 +
|
||||
this.padding27;
|
||||
return p1 + p2;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class LocalStack<T> {
|
||||
|
||||
private final Object[] elementData;
|
||||
private int elementCount;
|
||||
|
||||
public LocalStack(int capacity) {
|
||||
this.elementData = new Object[capacity];
|
||||
this.elementCount = 0;
|
||||
}
|
||||
|
||||
int size() {
|
||||
return this.elementCount;
|
||||
}
|
||||
|
||||
boolean empty() {
|
||||
return this.elementCount == 0;
|
||||
}
|
||||
|
||||
public void push(T elem) {
|
||||
this.elementData[this.elementCount++] = elem;
|
||||
}
|
||||
|
||||
public T pop() {
|
||||
if (this.elementCount == 0) {
|
||||
throw new EmptyStackException();
|
||||
}
|
||||
this.elementCount--;
|
||||
@SuppressWarnings("unchecked")
|
||||
T elem = (T) this.elementData[this.elementCount];
|
||||
this.elementData[this.elementCount] = null;
|
||||
return elem;
|
||||
}
|
||||
|
||||
public T peek() {
|
||||
if (this.elementCount == 0) {
|
||||
throw new EmptyStackException();
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
T elem = (T) this.elementData[this.elementCount - 1];
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class FastMap<K, V> {
|
||||
|
||||
private final Map<K, V> hashMap;
|
||||
|
||||
private K key1;
|
||||
private K key2;
|
||||
private K key3;
|
||||
|
||||
private V val1;
|
||||
private V val2;
|
||||
private V val3;
|
||||
|
||||
public FastMap() {
|
||||
this.hashMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.hashMap.size();
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return this.hashMap.containsKey(key);
|
||||
}
|
||||
|
||||
public V get(Object key) {
|
||||
if (key == this.key1) {
|
||||
return this.val1;
|
||||
} else if (key == this.key2) {
|
||||
return this.val2;
|
||||
} else if (key == this.key3) {
|
||||
return this.val3;
|
||||
}
|
||||
|
||||
return this.hashMap.get(key);
|
||||
}
|
||||
|
||||
public V put(K key, V value) {
|
||||
if (this.key1 == null) {
|
||||
this.key1 = key;
|
||||
this.val1 = value;
|
||||
} else if (this.key2 == null) {
|
||||
this.key2 = key;
|
||||
this.val2 = value;
|
||||
} else if (this.key3 == null) {
|
||||
this.key3 = key;
|
||||
this.val3 = value;
|
||||
}
|
||||
|
||||
return this.hashMap.put(key, value);
|
||||
}
|
||||
|
||||
public V remove(Object key) {
|
||||
if (key == this.key1) {
|
||||
this.key1 = null;
|
||||
this.val1 = null;
|
||||
} else if (key == this.key2) {
|
||||
this.key2 = null;
|
||||
this.val2 = null;
|
||||
} else if (key == this.key3) {
|
||||
this.key3 = null;
|
||||
this.val3 = null;
|
||||
}
|
||||
|
||||
return this.hashMap.remove(key);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.key1 = null;
|
||||
this.key2 = null;
|
||||
this.key3 = null;
|
||||
|
||||
this.val1 = null;
|
||||
this.val2 = null;
|
||||
this.val3 = null;
|
||||
|
||||
this.hashMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR })
|
||||
public @interface Watched {
|
||||
String value() default "";
|
||||
String prefix() default "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.perf;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface Stopwatch extends Cloneable {
|
||||
|
||||
Path id();
|
||||
|
||||
String name();
|
||||
|
||||
Path parent();
|
||||
|
||||
void startTime(long startTime);
|
||||
|
||||
void endTime(long startTime);
|
||||
|
||||
void lastStartTime(long startTime);
|
||||
|
||||
long times();
|
||||
|
||||
long totalTimes();
|
||||
|
||||
long totalChildrenTimes();
|
||||
|
||||
long totalCost();
|
||||
|
||||
void totalCost(long otherCost);
|
||||
|
||||
long minCost();
|
||||
|
||||
long maxCost();
|
||||
|
||||
long totalWasted();
|
||||
|
||||
long totalSelfWasted();
|
||||
|
||||
long totalChildrenWasted();
|
||||
|
||||
void fillChildrenTotal(List<Stopwatch> children);
|
||||
|
||||
Stopwatch copy();
|
||||
|
||||
Stopwatch child(String name);
|
||||
|
||||
Stopwatch child(String name, Stopwatch watch);
|
||||
|
||||
boolean empty();
|
||||
|
||||
void clear();
|
||||
|
||||
default String toJson() {
|
||||
int len = 200 + this.name().length() + this.parent().length();
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
sb.append("{");
|
||||
sb.append("\"parent\":\"").append(this.parent()).append("\"");
|
||||
sb.append(",\"name\":\"").append(this.name()).append("\"");
|
||||
sb.append(",\"times\":").append(this.times());
|
||||
sb.append(",\"total_cost\":").append(this.totalCost());
|
||||
sb.append(",\"min_cost\":").append(this.minCost());
|
||||
sb.append(",\"max_cost\":").append(this.maxCost());
|
||||
sb.append(",\"total_self_wasted\":").append(this.totalSelfWasted());
|
||||
sb.append(",\"total_children_wasted\":").append(
|
||||
this.totalChildrenWasted());
|
||||
sb.append(",\"total_children_times\":").append(
|
||||
this.totalChildrenTimes());
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static Path id(Path parent, String name) {
|
||||
if (parent == Path.EMPTY && name == Path.ROOT_NAME) {
|
||||
return Path.EMPTY;
|
||||
}
|
||||
return new Path(parent, name);
|
||||
}
|
||||
|
||||
final class Path implements Comparable<Path> {
|
||||
|
||||
public static final String ROOT_NAME = "root";
|
||||
public static final Path EMPTY = new Path("");
|
||||
|
||||
private final String path;
|
||||
|
||||
public Path(String self) {
|
||||
this.path = self;
|
||||
}
|
||||
|
||||
public Path(Path parent, String name) {
|
||||
if (parent == EMPTY) {
|
||||
this.path = name;
|
||||
} else {
|
||||
this.path = parent.path + '/' + name;
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return this.path.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.path.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this.hashCode() != obj.hashCode()) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof Path)) {
|
||||
return false;
|
||||
}
|
||||
Path other = (Path) obj;
|
||||
return this.path.equals(other.path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Path other) {
|
||||
return this.path.compareTo(other.path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public boolean endsWith(String name) {
|
||||
return this.path.endsWith(name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,479 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.rest;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hugegraph.util.JsonUtilCommon;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import okhttp3.ConnectionPool;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okio.BufferedSink;
|
||||
import okio.GzipSink;
|
||||
import okio.Okio;
|
||||
|
||||
/**
|
||||
* This class provides an abstract implementation of the RestClient interface.
|
||||
* It provides methods for making HTTP requests (GET, POST, PUT, DELETE) to a REST API.
|
||||
* Note: It uses the OkHttp library to make these requests for now.
|
||||
*/
|
||||
public abstract class AbstractRestClient implements RestClient {
|
||||
|
||||
private final ThreadLocal<String> authContext;
|
||||
|
||||
private final OkHttpClient client;
|
||||
|
||||
private final String baseUrl;
|
||||
|
||||
public AbstractRestClient(String url, int timeout) {
|
||||
this(url, RestClientConfig.builder().timeout(timeout).build());
|
||||
}
|
||||
|
||||
public AbstractRestClient(String url, String user, String password, int timeout) {
|
||||
this(url, RestClientConfig.builder()
|
||||
.user(user)
|
||||
.password(password)
|
||||
.timeout(timeout)
|
||||
.build());
|
||||
}
|
||||
|
||||
public AbstractRestClient(String url, int timeout, int idleTime,
|
||||
int maxConns, int maxConnsPerRoute) {
|
||||
this(url, RestClientConfig.builder()
|
||||
.idleTime(idleTime)
|
||||
.timeout(timeout)
|
||||
.maxConns(maxConns)
|
||||
.maxConnsPerRoute(maxConnsPerRoute)
|
||||
.build());
|
||||
}
|
||||
|
||||
public AbstractRestClient(String url, String user, String password, int timeout,
|
||||
int maxConns, int maxConnsPerRoute,
|
||||
String trustStoreFile, String trustStorePassword) {
|
||||
this(url, RestClientConfig.builder()
|
||||
.user(user).password(password)
|
||||
.timeout(timeout)
|
||||
.maxConns(maxConns)
|
||||
.maxConnsPerRoute(maxConnsPerRoute)
|
||||
.trustStoreFile(trustStoreFile)
|
||||
.trustStorePassword(trustStorePassword)
|
||||
.build());
|
||||
}
|
||||
|
||||
public AbstractRestClient(String url, String token, int timeout,
|
||||
int maxConns, int maxConnsPerRoute,
|
||||
String trustStoreFile, String trustStorePassword) {
|
||||
this(url, RestClientConfig.builder()
|
||||
.token(token)
|
||||
.timeout(timeout)
|
||||
.maxConns(maxConns)
|
||||
.maxConnsPerRoute(maxConnsPerRoute)
|
||||
.trustStoreFile(trustStoreFile)
|
||||
.trustStorePassword(trustStorePassword)
|
||||
.build());
|
||||
}
|
||||
|
||||
public AbstractRestClient(String url, RestClientConfig config) {
|
||||
this.baseUrl = url;
|
||||
this.client = buildOkHttpClient(config);
|
||||
this.authContext = new InheritableThreadLocal<>();
|
||||
}
|
||||
|
||||
private static RequestBody buildRequestBody(Object body, RestHeaders headers) {
|
||||
String contentType = parseContentType(headers);
|
||||
String bodyContent;
|
||||
if (RestHeaders.APPLICATION_JSON.equals(contentType)) {
|
||||
if (body == null) {
|
||||
bodyContent = "{}";
|
||||
} else if (body instanceof String) {
|
||||
bodyContent = (String) body;
|
||||
} else {
|
||||
bodyContent = JsonUtilCommon.toJson(body);
|
||||
}
|
||||
} else {
|
||||
bodyContent = String.valueOf(body);
|
||||
}
|
||||
RequestBody requestBody = RequestBody.create(bodyContent.getBytes(),
|
||||
MediaType.parse(contentType));
|
||||
|
||||
if (headers != null &&
|
||||
"gzip".equals(headers.get(RestHeaders.CONTENT_ENCODING))) {
|
||||
requestBody = gzipBody(requestBody);
|
||||
}
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
private static RequestBody gzipBody(final RequestBody body) {
|
||||
return new RequestBody() {
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return body.contentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return -1; // We don't know the compressed length in advance!
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(@NotNull BufferedSink sink) throws IOException {
|
||||
BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
|
||||
body.writeTo(gzipSink);
|
||||
gzipSink.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static String parseContentType(RestHeaders headers) {
|
||||
if (headers != null) {
|
||||
String contentType = headers.get(RestHeaders.CONTENT_TYPE);
|
||||
if (contentType != null) {
|
||||
return contentType;
|
||||
}
|
||||
}
|
||||
return RestHeaders.APPLICATION_JSON;
|
||||
}
|
||||
|
||||
private OkHttpClient buildOkHttpClient(RestClientConfig config) {
|
||||
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
||||
|
||||
if (config.getTimeout() != null) {
|
||||
builder.connectTimeout(config.getTimeout(), TimeUnit.MILLISECONDS)
|
||||
.readTimeout(config.getTimeout(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (config.getConnectTimeout() != null) {
|
||||
builder.connectTimeout(config.getConnectTimeout(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (config.getReadTimeout() != null) {
|
||||
builder.readTimeout(config.getReadTimeout(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
if (config.getMaxIdleConns() != null || config.getIdleTime() != null) {
|
||||
ConnectionPool connectionPool = new ConnectionPool(config.getMaxIdleConns(),
|
||||
config.getIdleTime(),
|
||||
TimeUnit.SECONDS);
|
||||
builder.connectionPool(connectionPool);
|
||||
}
|
||||
|
||||
// auth header interceptor
|
||||
if (StringUtils.isNotBlank(config.getUser()) &&
|
||||
StringUtils.isNotBlank(config.getPassword())) {
|
||||
builder.addInterceptor(new OkHttpBasicAuthInterceptor(config.getUser(),
|
||||
config.getPassword()));
|
||||
}
|
||||
if (StringUtils.isNotBlank(config.getToken())) {
|
||||
builder.addInterceptor(new OkHttpTokenInterceptor(config.getToken()));
|
||||
}
|
||||
|
||||
// ssl
|
||||
configSsl(builder, this.baseUrl, config.getTrustStoreFile(),
|
||||
config.getTrustStorePassword());
|
||||
|
||||
// Execute builder callback before builder.build() for user configs
|
||||
if (config.getBuilderCallback() != null) {
|
||||
config.getBuilderCallback().accept(builder);
|
||||
}
|
||||
|
||||
OkHttpClient okHttpClient = builder.build();
|
||||
|
||||
if (config.getMaxConns() != null) {
|
||||
okHttpClient.dispatcher().setMaxRequests(config.getMaxConns());
|
||||
}
|
||||
|
||||
if (config.getMaxConnsPerRoute() != null) {
|
||||
okHttpClient.dispatcher().setMaxRequestsPerHost(config.getMaxConnsPerRoute());
|
||||
}
|
||||
|
||||
return okHttpClient;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void configSsl(OkHttpClient.Builder builder, String url, String trustStoreFile,
|
||||
String trustStorePass) {
|
||||
if (StringUtils.isBlank(trustStoreFile) || StringUtils.isBlank(trustStorePass)) {
|
||||
return;
|
||||
}
|
||||
|
||||
X509TrustManager trustManager = trustManagerForCertificates(trustStoreFile, trustStorePass);
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, new TrustManager[]{trustManager}, null);
|
||||
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||
|
||||
builder.sslSocketFactory(sslSocketFactory, trustManager)
|
||||
.hostnameVerifier(new HostNameVerifier(url));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestResult post(String path, Object object) {
|
||||
return this.post(path, object, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestResult post(String path, Object object, RestHeaders headers) {
|
||||
return this.post(path, object, headers, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestResult post(String path, Object object, Map<String, Object> params) {
|
||||
return this.post(path, object, null, params);
|
||||
}
|
||||
|
||||
private Request.Builder genRequestBuilder(String path, String id, RestHeaders headers,
|
||||
Map<String, Object> params) {
|
||||
HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(this.baseUrl))
|
||||
.newBuilder()
|
||||
.addPathSegments(path);
|
||||
if (id != null) {
|
||||
urlBuilder.addPathSegment(id);
|
||||
}
|
||||
|
||||
if (params != null) {
|
||||
params.forEach((name, value) -> {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof Collection) {
|
||||
for (Object i : (Collection<?>) value) {
|
||||
urlBuilder.addQueryParameter(name, String.valueOf(i));
|
||||
}
|
||||
} else {
|
||||
urlBuilder.addQueryParameter(name, String.valueOf(value));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Request.Builder builder = newRequestBuilder().url(urlBuilder.build());
|
||||
|
||||
if (headers != null) {
|
||||
builder.headers(headers.toOkHttpHeader());
|
||||
}
|
||||
|
||||
this.attachAuthToRequest(builder);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* In order to provide subclasses with overloading opportunities
|
||||
*/
|
||||
protected Request.Builder newRequestBuilder() {
|
||||
return new Request.Builder();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public RestResult post(String path, Object object, RestHeaders headers,
|
||||
Map<String, Object> params) {
|
||||
Request.Builder requestBuilder = genRequestBuilder(path, null, headers, params);
|
||||
requestBuilder.post(buildRequestBody(object, headers));
|
||||
|
||||
try (Response response = request(requestBuilder)) {
|
||||
checkStatus(response, 200, 201, 202);
|
||||
return new RestResult(response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestResult put(String path, String id, Object object) {
|
||||
return this.put(path, id, object, ImmutableMap.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestResult put(String path, String id, Object object, RestHeaders headers) {
|
||||
return this.put(path, id, object, headers, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestResult put(String path, String id, Object object, Map<String, Object> params) {
|
||||
return this.put(path, id, object, null, params);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public RestResult put(String path, String id, Object object,
|
||||
RestHeaders headers,
|
||||
Map<String, Object> params) {
|
||||
Request.Builder requestBuilder = genRequestBuilder(path, id, headers, params);
|
||||
requestBuilder.put(buildRequestBody(object, headers));
|
||||
|
||||
try (Response response = request(requestBuilder)) {
|
||||
checkStatus(response, 200, 202);
|
||||
return new RestResult(response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestResult get(String path) {
|
||||
return this.get(path, null, ImmutableMap.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestResult get(String path, Map<String, Object> params) {
|
||||
return this.get(path, null, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestResult get(String path, String id) {
|
||||
return this.get(path, id, ImmutableMap.of());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private RestResult get(String path, String id, Map<String, Object> params) {
|
||||
Request.Builder requestBuilder = genRequestBuilder(path, id, null, params);
|
||||
|
||||
try (Response response = request(requestBuilder)) {
|
||||
checkStatus(response, 200);
|
||||
return new RestResult(response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestResult delete(String path, Map<String, Object> params) {
|
||||
return this.delete(path, null, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestResult delete(String path, String id) {
|
||||
return this.delete(path, id, ImmutableMap.of());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private RestResult delete(String path, String id,
|
||||
Map<String, Object> params) {
|
||||
Request.Builder requestBuilder = genRequestBuilder(path, id, null, params);
|
||||
requestBuilder.delete();
|
||||
|
||||
try (Response response = request(requestBuilder)) {
|
||||
checkStatus(response, 204, 202);
|
||||
return new RestResult(response);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void checkStatus(Response response, int... statuses);
|
||||
|
||||
@SneakyThrows
|
||||
protected Response request(Request.Builder requestBuilder) {
|
||||
return this.client.newCall(requestBuilder.build()).execute();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.client != null) {
|
||||
this.client.dispatcher().executorService().shutdown();
|
||||
this.client.connectionPool().evictAll();
|
||||
if (this.client.cache() != null) {
|
||||
this.client.cache().close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void resetAuthContext() {
|
||||
this.authContext.remove();
|
||||
}
|
||||
|
||||
public String getAuthContext() {
|
||||
return this.authContext.get();
|
||||
}
|
||||
|
||||
public void setAuthContext(String auth) {
|
||||
this.authContext.set(auth);
|
||||
}
|
||||
|
||||
private void attachAuthToRequest(Request.Builder builder) {
|
||||
// Add auth header
|
||||
String auth = this.getAuthContext();
|
||||
if (StringUtils.isNotEmpty(auth)) {
|
||||
builder.addHeader(RestHeaders.AUTHORIZATION, auth);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private X509TrustManager trustManagerForCertificates(String trustStoreFile,
|
||||
String trustStorePass) {
|
||||
char[] password = trustStorePass.toCharArray();
|
||||
|
||||
// load keyStore
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
try (FileInputStream in = new FileInputStream(trustStoreFile)) {
|
||||
keyStore.load(in, password);
|
||||
}
|
||||
|
||||
TrustManagerFactory trustManagerFactory =
|
||||
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(keyStore);
|
||||
|
||||
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
|
||||
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
|
||||
throw new IllegalStateException("Unexpected default trust managers:" +
|
||||
Arrays.toString(trustManagers));
|
||||
}
|
||||
return (X509TrustManager) trustManagers[0];
|
||||
}
|
||||
|
||||
public static class HostNameVerifier implements HostnameVerifier {
|
||||
|
||||
private final String url;
|
||||
|
||||
public HostNameVerifier(String url) {
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
url = "http://" + url;
|
||||
}
|
||||
url = URI.create(url).getHost();
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
if (!this.url.isEmpty() && this.url.endsWith(hostname)) {
|
||||
return true;
|
||||
} else {
|
||||
HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
return verifier.verify(hostname, session);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.rest;
|
||||
|
||||
public class ClientException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 814572040103754705L;
|
||||
|
||||
public ClientException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ClientException(String message, Object... args) {
|
||||
super(String.format(message, args));
|
||||
}
|
||||
|
||||
public ClientException(String message, Throwable cause, Object... args) {
|
||||
super(String.format(message, args), cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.Credentials;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class OkHttpBasicAuthInterceptor implements Interceptor {
|
||||
|
||||
private final String credentials;
|
||||
|
||||
public OkHttpBasicAuthInterceptor(String user, String password) {
|
||||
this.credentials = Credentials.basic(user, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request request = chain.request();
|
||||
if (request.header(RestHeaders.AUTHORIZATION) == null) {
|
||||
Request authenticatedRequest = request.newBuilder()
|
||||
.header(RestHeaders.AUTHORIZATION,
|
||||
this.credentials)
|
||||
.build();
|
||||
return chain.proceed(authenticatedRequest);
|
||||
}
|
||||
return chain.proceed(request);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.rest;
|
||||
|
||||
import static org.apache.hugegraph.rest.RestHeaders.AUTHORIZATION;
|
||||
import static org.apache.hugegraph.rest.RestHeaders.BEARER_PREFIX;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
|
||||
public class OkHttpTokenInterceptor implements Interceptor {
|
||||
|
||||
private final String token;
|
||||
|
||||
public OkHttpTokenInterceptor(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request request = chain.request();
|
||||
if (request.header(AUTHORIZATION) == null) {
|
||||
Request authenticatedRequest = request.newBuilder()
|
||||
.header(AUTHORIZATION,
|
||||
BEARER_PREFIX + this.token)
|
||||
.build();
|
||||
return chain.proceed(authenticatedRequest);
|
||||
}
|
||||
return chain.proceed(request);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.rest;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface RestClient {
|
||||
/**
|
||||
* Post method
|
||||
*/
|
||||
RestResult post(String path, Object object);
|
||||
|
||||
RestResult post(String path, Object object, RestHeaders headers);
|
||||
|
||||
RestResult post(String path, Object object, Map<String, Object> params);
|
||||
|
||||
RestResult post(String path, Object object, RestHeaders headers, Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* Put method
|
||||
*/
|
||||
RestResult put(String path, String id, Object object);
|
||||
|
||||
RestResult put(String path, String id, Object object, RestHeaders headers);
|
||||
|
||||
RestResult put(String path, String id, Object object, Map<String, Object> params);
|
||||
|
||||
RestResult put(String path, String id, Object object, RestHeaders headers,
|
||||
Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* Get method
|
||||
*/
|
||||
RestResult get(String path);
|
||||
|
||||
RestResult get(String path, Map<String, Object> params);
|
||||
|
||||
RestResult get(String path, String id);
|
||||
|
||||
/**
|
||||
* Delete method
|
||||
*/
|
||||
RestResult delete(String path, Map<String, Object> params);
|
||||
|
||||
RestResult delete(String path, String id);
|
||||
|
||||
void close();
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.rest;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@Setter
|
||||
@SuppressWarnings("unused")
|
||||
public class RestClientConfig {
|
||||
|
||||
private String user;
|
||||
private String password;
|
||||
private String token;
|
||||
/**
|
||||
* @deprecated use connectTimeout and readTimeout instead
|
||||
*/
|
||||
@Deprecated
|
||||
private Integer timeout;
|
||||
/** unit in milliseconds */
|
||||
private Integer connectTimeout;
|
||||
/** unit in milliseconds */
|
||||
private Integer readTimeout;
|
||||
private Integer maxConns;
|
||||
private Integer maxConnsPerRoute;
|
||||
// unit in seconds
|
||||
private Integer idleTime = 30;
|
||||
private Integer maxIdleConns = 5;
|
||||
private String trustStoreFile;
|
||||
private String trustStorePassword;
|
||||
private Consumer<OkHttpClient.Builder> builderCallback;
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.rest;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
|
||||
import kotlin.Pair;
|
||||
|
||||
public class RestHeaders {
|
||||
|
||||
public static final String CONTENT_TYPE = "Content-Type";
|
||||
|
||||
public static final String CONTENT_ENCODING = "Content-Encoding";
|
||||
|
||||
public static final String AUTHORIZATION = "Authorization";
|
||||
|
||||
public static final String APPLICATION_JSON = "application/json";
|
||||
|
||||
public static final String BEARER_PREFIX = "Bearer ";
|
||||
|
||||
private final okhttp3.Headers.Builder headersBuilder;
|
||||
|
||||
public RestHeaders() {
|
||||
this.headersBuilder = new okhttp3.Headers.Builder();
|
||||
}
|
||||
|
||||
public static RestHeaders convertToRestHeaders(okhttp3.Headers headers) {
|
||||
RestHeaders restHeaders = new RestHeaders();
|
||||
|
||||
if (headers != null) {
|
||||
Iterator<Pair<String, String>> iter = headers.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Pair<String, String> pair = iter.next();
|
||||
restHeaders.add(pair.getFirst(), pair.getSecond());
|
||||
}
|
||||
}
|
||||
return restHeaders;
|
||||
}
|
||||
|
||||
public String get(String key) {
|
||||
return this.headersBuilder.get(key);
|
||||
}
|
||||
|
||||
public Date getDate(String key) {
|
||||
return this.headersBuilder.build().getDate(key);
|
||||
}
|
||||
|
||||
public RestHeaders add(String key, String value) {
|
||||
this.headersBuilder.add(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RestHeaders add(String key, Date value) {
|
||||
this.headersBuilder.add(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.toOkHttpHeader().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof RestHeaders) {
|
||||
return this.toOkHttpHeader().equals(((RestHeaders) obj).toOkHttpHeader());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public okhttp3.Headers toOkHttpHeader() {
|
||||
return this.headersBuilder.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class RestResult {
|
||||
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
private final int status;
|
||||
private final RestHeaders headers;
|
||||
private final String content;
|
||||
|
||||
public RestResult(Response response) {
|
||||
this(response.code(), getResponseContent(response),
|
||||
RestHeaders.convertToRestHeaders(response.headers()));
|
||||
}
|
||||
|
||||
public RestResult(int status, String content, RestHeaders headers) {
|
||||
this.status = status;
|
||||
this.headers = headers;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static String getResponseContent(Response response) {
|
||||
return response.body().string();
|
||||
}
|
||||
|
||||
public static void registerModule(Module module) {
|
||||
MAPPER.registerModule(module);
|
||||
}
|
||||
|
||||
public int status() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public RestHeaders headers() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
public String content() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
public <T> T readObject(Class<T> clazz) {
|
||||
try {
|
||||
return MAPPER.readValue(this.content, clazz);
|
||||
} catch (Exception e) {
|
||||
throw new SerializeException("Failed to deserialize: %s", e, this.content);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public <T> List<T> readList(String key, Class<T> clazz) {
|
||||
try {
|
||||
JsonNode root = MAPPER.readTree(this.content);
|
||||
JsonNode element = root.get(key);
|
||||
if (element == null) {
|
||||
throw new SerializeException("Can't find value of the key: %s in json.", key);
|
||||
}
|
||||
JavaType type = MAPPER.getTypeFactory()
|
||||
.constructParametrizedType(ArrayList.class,
|
||||
List.class, clazz);
|
||||
return MAPPER.convertValue(element, type);
|
||||
} catch (IOException e) {
|
||||
throw new SerializeException("Failed to deserialize %s", e, this.content);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public <T> List<T> readList(Class<T> clazz) {
|
||||
JavaType type = MAPPER.getTypeFactory()
|
||||
.constructParametrizedType(ArrayList.class,
|
||||
List.class, clazz);
|
||||
try {
|
||||
return MAPPER.readValue(this.content, type);
|
||||
} catch (IOException e) {
|
||||
throw new SerializeException("Failed to deserialize %s", e, this.content);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{status=%s, headers=%s, content=%s}", this.status, this.headers,
|
||||
this.content);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.rest;
|
||||
|
||||
public class SerializeException extends ClientException {
|
||||
|
||||
private static final long serialVersionUID = -4622753445618619311L;
|
||||
|
||||
public SerializeException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
|
||||
public SerializeException(String message, Object... args) {
|
||||
super(message, args);
|
||||
}
|
||||
|
||||
public SerializeException(String message, Throwable e, Object... args) {
|
||||
super(message, e, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.testutil;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.hamcrest.Description;
|
||||
|
||||
public class Assert extends org.junit.Assert {
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ThrowableRunnable {
|
||||
void run() throws Throwable;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ThrowableConsumer<T> {
|
||||
void accept(T t) throws Throwable;
|
||||
}
|
||||
|
||||
public static void assertThrows(Class<? extends Throwable> clazz,
|
||||
ThrowableRunnable runnable,
|
||||
Consumer<Throwable> exceptionConsumer) {
|
||||
Throwable expectedException = assertThrows(clazz, runnable);
|
||||
assert expectedException != null;
|
||||
exceptionConsumer.accept(expectedException);
|
||||
}
|
||||
|
||||
public static Throwable assertThrows(Class<? extends Throwable> clazz,
|
||||
ThrowableRunnable runnable) {
|
||||
try {
|
||||
// expect throwing here
|
||||
runnable.run();
|
||||
} catch (Throwable e) {
|
||||
if (!clazz.isInstance(e)) {
|
||||
// exception type not matched
|
||||
Assert.fail(String.format("Bad exception type %s(expected %s)",
|
||||
e.getClass().getName(), clazz.getName()));
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
// no exception
|
||||
Assert.fail(String.format("No exception was thrown(expected %s)",
|
||||
clazz.getName()));
|
||||
|
||||
// unavailable
|
||||
assert false;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void assertEquals(byte expected, Object actual) {
|
||||
org.junit.Assert.assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public static void assertEquals(short expected, Object actual) {
|
||||
org.junit.Assert.assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public static void assertEquals(char expected, Object actual) {
|
||||
org.junit.Assert.assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public static void assertEquals(int expected, Object actual) {
|
||||
org.junit.Assert.assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public static void assertEquals(long expected, Object actual) {
|
||||
org.junit.Assert.assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public static void assertEquals(float expected, Object actual) {
|
||||
org.junit.Assert.assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public static void assertEquals(double expected, Object actual) {
|
||||
org.junit.Assert.assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void assertGt(Number expected, Object actual) {
|
||||
org.junit.Assert.assertThat(actual, new NumberMatcher(expected, cmp -> {
|
||||
return cmp > 0;
|
||||
}, ">"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void assertGte(Number expected, Object actual) {
|
||||
org.junit.Assert.assertThat(actual, new NumberMatcher(expected, cmp -> {
|
||||
return cmp >= 0;
|
||||
}, ">="));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void assertLt(Number expected, Object actual) {
|
||||
org.junit.Assert.assertThat(actual, new NumberMatcher(expected, cmp -> {
|
||||
return cmp < 0;
|
||||
}, "<"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void assertLte(Number expected, Object actual) {
|
||||
org.junit.Assert.assertThat(actual, new NumberMatcher(expected, cmp -> {
|
||||
return cmp <= 0;
|
||||
}, "<="));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void assertContains(String sub, String actual) {
|
||||
org.junit.Assert.assertThat(actual, CoreMatchers.containsString(sub));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void assertInstanceOf(Class<?> clazz, Object object) {
|
||||
org.junit.Assert.assertThat(object, CoreMatchers.instanceOf(clazz));
|
||||
}
|
||||
|
||||
private static class NumberMatcher extends BaseMatcher<Object> {
|
||||
|
||||
private final String symbol;
|
||||
private final Number expected;
|
||||
private final Function<Integer, Boolean> cmp;
|
||||
|
||||
NumberMatcher(Number expected, Function<Integer, Boolean> cmp, String symbol) {
|
||||
this.expected = expected;
|
||||
this.cmp = cmp;
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean matches(Object actual) {
|
||||
Assert.assertInstanceOf(this.expected.getClass(), actual);
|
||||
Assert.assertInstanceOf(Comparable.class, actual);
|
||||
int cmp = ((Comparable<Number>) actual).compareTo(this.expected);
|
||||
return this.cmp.apply(cmp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description desc) {
|
||||
desc.appendText("a number ").appendText(this.symbol)
|
||||
.appendText(" ").appendText(this.expected.toString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.testutil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.hugegraph.util.E;
|
||||
import com.google.common.primitives.Primitives;
|
||||
|
||||
public class Whitebox {
|
||||
|
||||
public static final char SEPARATOR = '.';
|
||||
|
||||
public static void setInternalState(Object target,
|
||||
String fieldName, Object value) {
|
||||
assert target != null;
|
||||
assert fieldName != null;
|
||||
int sep = fieldName.lastIndexOf(SEPARATOR);
|
||||
if (sep > 0) {
|
||||
String prefix = fieldName.substring(0, sep);
|
||||
Object result = getInternalState(target, prefix);
|
||||
E.checkArgument(result != null,
|
||||
"Can't set value on null field: `%s.%s`",
|
||||
target, prefix);
|
||||
target = result;
|
||||
fieldName = fieldName.substring(sep + 1);
|
||||
}
|
||||
|
||||
Class<?> c = target instanceof Class<?> ?
|
||||
(Class<?>) target : target.getClass();
|
||||
try {
|
||||
Field f = getFieldFromHierarchy(c, fieldName);
|
||||
f.setAccessible(true);
|
||||
f.set(target, value);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Can't set value of '%s' against object '%s'",
|
||||
fieldName, target), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T getInternalState(Object target, String fieldName) {
|
||||
assert fieldName != null;
|
||||
int sep = fieldName.indexOf(SEPARATOR);
|
||||
if (sep > 0) {
|
||||
String field = fieldName.substring(0, sep);
|
||||
Object value = getInternalState(target, field);
|
||||
field = fieldName.substring(sep + 1);
|
||||
return getInternalState(value, field);
|
||||
}
|
||||
|
||||
Class<?> c = target instanceof Class<?> ?
|
||||
(Class<?>) target : target.getClass();
|
||||
try {
|
||||
Field f = getFieldFromHierarchy(c, fieldName);
|
||||
f.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) f.get(target);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Unable to get internal state on field '%s' of %s",
|
||||
fieldName, target), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Field getFieldFromHierarchy(Class<?> clazz, String field) {
|
||||
Field f = getField(clazz, field);
|
||||
while (f == null && clazz != Object.class) {
|
||||
clazz = clazz.getSuperclass();
|
||||
f = getField(clazz, field);
|
||||
}
|
||||
if (f == null) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Not declared field '%s' in class '%s'",
|
||||
field, clazz.getSimpleName()));
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
private static Field getField(Class<?> clazz, String field) {
|
||||
try {
|
||||
return clazz.getDeclaredField(field);
|
||||
} catch (NoSuchFieldException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T invokeStatic(Class<?> clazz, String methodName,
|
||||
Object... args) {
|
||||
return invoke(clazz, methodName, (Object) null, args);
|
||||
}
|
||||
|
||||
public static <T> T invokeStatic(Class<?> clazz, Class<?>[] classes,
|
||||
String methodName, Object... args) {
|
||||
return invoke(clazz, classes, methodName, (Object) null, args);
|
||||
}
|
||||
|
||||
public static <T> T invoke(Object owner, String field,
|
||||
String methodName, Object... args) {
|
||||
Object self = getInternalState(owner, field);
|
||||
Objects.requireNonNull(self);
|
||||
return invoke(self.getClass(), methodName, self, args);
|
||||
}
|
||||
|
||||
public static <T> T invoke(Object owner, String field, Class<?>[] classes,
|
||||
String methodName, Object... args) {
|
||||
Object self = getInternalState(owner, field);
|
||||
Objects.requireNonNull(self);
|
||||
return invoke(self.getClass(), classes, methodName, self, args);
|
||||
}
|
||||
|
||||
public static <T> T invoke(Class<?> clazz, String methodName,
|
||||
Object self, Object... args) {
|
||||
Class<?>[] classes = new Class<?>[args.length];
|
||||
int i = 0;
|
||||
for (Object arg : args) {
|
||||
E.checkArgument(arg != null, "The argument can't be null");
|
||||
classes[i++] = Primitives.unwrap(arg.getClass());
|
||||
}
|
||||
return invoke(clazz, classes, methodName, self, args);
|
||||
}
|
||||
|
||||
public static <T> T invoke(Class<?> clazz, Class<?>[] classes,
|
||||
String methodName, Object self, Object... args) {
|
||||
Method method = method(clazz, methodName, classes);
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) method.invoke(self, args);
|
||||
return result;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Can't invoke method '%s' of class '%s': %s",
|
||||
methodName, clazz, e.getMessage()), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Throwable target = e.getTargetException();
|
||||
if (target instanceof RuntimeException) {
|
||||
throw (RuntimeException) target;
|
||||
}
|
||||
throw new RuntimeException(String.format(
|
||||
"Can't invoke method '%s' of class '%s': %s",
|
||||
methodName, clazz, target.getMessage()), target);
|
||||
}
|
||||
}
|
||||
|
||||
public static Method method(Class<?> clazz, String methodName,
|
||||
Class<?>[] argsClasses) {
|
||||
try {
|
||||
return clazz.getDeclaredMethod(methodName, argsClasses);
|
||||
} catch (NoSuchMethodException e) {
|
||||
Class<?> superclass = clazz.getSuperclass();
|
||||
if (superclass != null) {
|
||||
try {
|
||||
return method(superclass, methodName, argsClasses);
|
||||
} catch (Exception ignored) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
throw new RuntimeException(String.format(
|
||||
"Can't find method '%s' with args %s of class '%s'",
|
||||
methodName, Arrays.asList(argsClasses), clazz), e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
|
||||
import com.google.common.primitives.UnsignedBytes;
|
||||
|
||||
/**
|
||||
* TODO: extends com.google.common.primitives.Bytes
|
||||
*/
|
||||
public final class Bytes {
|
||||
|
||||
public static final long BASE = 1024L;
|
||||
public static final long KB = BASE;
|
||||
public static final long MB = KB * BASE;
|
||||
public static final long GB = MB * BASE;
|
||||
public static final long TB = GB * KB;
|
||||
public static final long PB = GB * MB;
|
||||
public static final long EB = GB * GB;
|
||||
|
||||
private static final Comparator<byte[]> CMP = UnsignedBytes.lexicographicalComparator();
|
||||
|
||||
public static int compare(byte[] bytes1, byte[] bytes2) {
|
||||
return CMP.compare(bytes1, bytes2);
|
||||
}
|
||||
|
||||
public static byte[] concat(byte[] bytes1, byte[] bytes2) {
|
||||
byte[] result = new byte[bytes1.length + bytes2.length];
|
||||
System.arraycopy(bytes1, 0, result, 0, bytes1.length);
|
||||
System.arraycopy(bytes2, 0, result, bytes1.length, bytes2.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean prefixWith(byte[] bytes, byte[] prefix) {
|
||||
if (bytes.length < prefix.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < prefix.length; i++) {
|
||||
if (bytes[i] != prefix[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean contains(byte[] bytes, byte value) {
|
||||
for (byte b : bytes) {
|
||||
if (b == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int indexOf(byte[] bytes, byte value) {
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
if (bytes[i] == value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static boolean equals(byte[] bytes1, byte[] bytes2) {
|
||||
return Arrays.equals(bytes1, bytes2);
|
||||
}
|
||||
|
||||
public static String toHex(byte b) {
|
||||
return toHex(new byte[]{b});
|
||||
}
|
||||
|
||||
public static String toHex(byte[] bytes) {
|
||||
return new String(Hex.encodeHex(bytes));
|
||||
}
|
||||
|
||||
public static byte[] fromHex(String hex) {
|
||||
try {
|
||||
return Hex.decodeHex(hex.toCharArray());
|
||||
} catch (DecoderException e) {
|
||||
throw new RuntimeException("Failed to decode hex: " + hex, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import static java.lang.System.exit;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
public final class CheckSocket {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// Check if the socket connection can be closed normally
|
||||
new Socket(InetAddress.getByName(args[0]), Integer.parseInt(args[1])).close();
|
||||
exit(0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,448 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class CollectionUtil {
|
||||
|
||||
public static <T> Set<T> toSet(Object object) {
|
||||
E.checkNotNull(object, "object");
|
||||
Set<T> set = InsertionOrderUtil.newSet();
|
||||
fillCollection(set, object);
|
||||
return set;
|
||||
}
|
||||
|
||||
public static <T> List<T> toList(Object object) {
|
||||
E.checkNotNull(object, "object");
|
||||
List<T> list = new ArrayList<>();
|
||||
fillCollection(list, object);
|
||||
return list;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> void fillCollection(Collection<T> collection,
|
||||
Object object) {
|
||||
if (object.getClass().isArray()) {
|
||||
int length = Array.getLength(object);
|
||||
for (int i = 0; i < length; i++) {
|
||||
collection.add((T) Array.get(object, i));
|
||||
}
|
||||
} else if (object instanceof Collection) {
|
||||
collection.addAll((Collection<T>) object);
|
||||
} else {
|
||||
collection.add((T) object);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> boolean prefixOf(List<T> prefix, List<T> all) {
|
||||
E.checkNotNull(prefix, "prefix");
|
||||
E.checkNotNull(all, "all");
|
||||
|
||||
if (prefix.size() > all.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < prefix.size(); i++) {
|
||||
T first = prefix.get(i);
|
||||
T second = all.get(i);
|
||||
if (first == second) {
|
||||
continue;
|
||||
}
|
||||
if (first == null || !first.equals(second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Set<Integer> randomSet(int min, int max, int count) {
|
||||
E.checkArgument(max > min, "Invalid min/max: %s/%s", min, max);
|
||||
E.checkArgument(0 < count && count <= max - min,
|
||||
"Invalid count %s", count);
|
||||
|
||||
Set<Integer> randoms = new HashSet<>();
|
||||
while (randoms.size() < count) {
|
||||
randoms.add(ThreadLocalRandom.current().nextInt(min, max));
|
||||
}
|
||||
return randoms;
|
||||
}
|
||||
|
||||
public static boolean allUnique(Collection<?> collection) {
|
||||
HashSet<Object> set = new HashSet<>(collection.size());
|
||||
for (Object elem : collection) {
|
||||
if (!set.add(elem)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sub-set of a set.
|
||||
* @param original original set
|
||||
* @param from index of start position
|
||||
* @param to index of end position(exclude), but -1 means the last element
|
||||
* @param <T> element type of set
|
||||
* @return sub-set of original set [from, to)
|
||||
*/
|
||||
public static <T> Set<T> subSet(Set<T> original, int from, int to) {
|
||||
E.checkArgument(from >= 0,
|
||||
"Invalid from parameter of subSet(): %s", from);
|
||||
if (to < 0) {
|
||||
to = original.size();
|
||||
} else {
|
||||
E.checkArgument(to >= from,
|
||||
"Invalid to parameter of subSet(): %s", to);
|
||||
}
|
||||
List<T> list = new ArrayList<>(original);
|
||||
return new LinkedHashSet<>(list.subList(from, to));
|
||||
}
|
||||
|
||||
public static <T> Set<T> union(Collection<T> first, Collection<T> second) {
|
||||
E.checkNotNull(first, "first");
|
||||
E.checkNotNull(second, "second");
|
||||
HashSet<T> results = new HashSet<>(first);
|
||||
results.addAll(second);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the intersection of two collections, the original collections
|
||||
* will not be modified
|
||||
* @param first the first collection
|
||||
* @param second the second collection
|
||||
* @param <T> element type of collection
|
||||
* @return intersection of the two collections
|
||||
*/
|
||||
public static <T> Collection<T> intersect(Collection<T> first,
|
||||
Collection<T> second) {
|
||||
E.checkNotNull(first, "first");
|
||||
E.checkNotNull(second, "second");
|
||||
|
||||
HashSet<T> results;
|
||||
if (first instanceof HashSet) {
|
||||
@SuppressWarnings("unchecked")
|
||||
HashSet<T> clone = (HashSet<T>) ((HashSet<T>) first).clone();
|
||||
results = clone;
|
||||
} else {
|
||||
results = new HashSet<>(first);
|
||||
}
|
||||
results.retainAll(second);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the intersection of two collections, note that the first collection
|
||||
* will be modified and store the results
|
||||
* @param first the first collection, it will be modified
|
||||
* @param second the second collection
|
||||
* @param <T> element type of collection
|
||||
* @return intersection of the two collections
|
||||
*/
|
||||
public static <T> Collection<T> intersectWithModify(Collection<T> first,
|
||||
Collection<T> second) {
|
||||
E.checkNotNull(first, "first");
|
||||
E.checkNotNull(second, "second");
|
||||
first.retainAll(second);
|
||||
return first;
|
||||
}
|
||||
|
||||
public static <T> boolean hasIntersection(List<T> first, Set<T> second) {
|
||||
E.checkNotNull(first, "first");
|
||||
E.checkNotNull(second, "second");
|
||||
for (T firstElem : first) {
|
||||
if (second.contains(firstElem)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T> boolean hasIntersection(Set<T> first, Set<T> second) {
|
||||
E.checkNotNull(first, "first");
|
||||
E.checkNotNull(second, "second");
|
||||
if (first.size() <= second.size()) {
|
||||
for (T firstElem : first) {
|
||||
if (second.contains(firstElem)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (T secondElem : second) {
|
||||
if (first.contains(secondElem)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <K extends Comparable<? super K>, V> Map<K, V> sortByKey(
|
||||
Map<K, V> map, boolean incr) {
|
||||
List<Map.Entry<K, V>> list = new ArrayList<>(map.entrySet());
|
||||
if (incr) {
|
||||
list.sort(Map.Entry.comparingByKey());
|
||||
} else {
|
||||
list.sort(Collections.reverseOrder(Map.Entry.comparingByKey()));
|
||||
}
|
||||
|
||||
Map<K, V> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<K, V> entry : list) {
|
||||
result.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(
|
||||
Map<K, V> map, boolean incr) {
|
||||
List<Map.Entry<K, V>> list = new ArrayList<>(map.entrySet());
|
||||
if (incr) {
|
||||
list.sort(Map.Entry.comparingByValue());
|
||||
} else {
|
||||
list.sort(Collections.reverseOrder(Map.Entry.comparingByValue()));
|
||||
}
|
||||
|
||||
Map<K, V> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<K, V> entry : list) {
|
||||
result.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cross combine the A(n, n) combinations of each part in parts
|
||||
* @param parts a list of part, like [{a,b}, {1,2}, {x,y}]
|
||||
* @return List<List<Integer>> all combinations like
|
||||
* [{a,b,1,2,x,y}, {a,b,1,2,y,x}, {a,b,2,1,x,y}, {a,b,2,1,y,x}...]
|
||||
*/
|
||||
public static <T> List<List<T>> crossCombineParts(List<List<T>> parts) {
|
||||
List<List<T>> results = new ArrayList<>();
|
||||
Deque<List<T>> selected = new ArrayDeque<>();
|
||||
crossCombineParts(parts, 0, selected, results);
|
||||
return results;
|
||||
}
|
||||
|
||||
private static <T> void crossCombineParts(List<List<T>> parts,
|
||||
int level,
|
||||
Deque<List<T>> selected,
|
||||
List<List<T>> results) {
|
||||
assert level < parts.size();
|
||||
List<T> part = parts.get(level);
|
||||
for (List<T> combination : anm(part)) {
|
||||
selected.addLast(combination);
|
||||
|
||||
if (level < parts.size() - 1) {
|
||||
crossCombineParts(parts, level + 1, selected, results);
|
||||
} else if (level == parts.size() - 1) {
|
||||
results.add(flatToList(selected));
|
||||
}
|
||||
|
||||
selected.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> List<T> flatToList(Deque<List<T>> parts) {
|
||||
List<T> list = new ArrayList<>();
|
||||
for (List<T> part : parts) {
|
||||
list.addAll(part);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse C(n, m) combinations of a list
|
||||
* @param all list to contain all items for combination
|
||||
* @param n m of C(n, m)
|
||||
* @param m n of C(n, m)
|
||||
* @return List<List<T>> all combinations
|
||||
*/
|
||||
public static <T> List<List<T>> cnm(List<T> all, int n, int m) {
|
||||
List<List<T>> combs = new ArrayList<>();
|
||||
cnm(all, n, m, comb -> {
|
||||
combs.add(comb);
|
||||
return false;
|
||||
});
|
||||
return combs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse C(n, m) combinations of a list to find first matched
|
||||
* result combination and call back with the result.
|
||||
* @param all list to contain all items for combination
|
||||
* @param n m of C(n, m)
|
||||
* @param m n of C(n, m)
|
||||
* @return true if matched any kind of items combination else false, the
|
||||
* callback can always return false if you want to traverse all combinations
|
||||
*/
|
||||
public static <T> boolean cnm(List<T> all, int n, int m,
|
||||
Function<List<T>, Boolean> callback) {
|
||||
return cnm(all, n, m, 0, null, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse C(n, m) combinations of a list to find first matched
|
||||
* result combination and call back with the result.
|
||||
* @param all list to contain all items for combination
|
||||
* @param n n of C(n, m)
|
||||
* @param m m of C(n, m)
|
||||
* @param current current position in list
|
||||
* @param selected list to contain selected items
|
||||
* @return true if matched any kind of items combination else false, the
|
||||
* callback can always return false if you want to traverse all combinations
|
||||
*/
|
||||
private static <T> boolean cnm(List<T> all, int n, int m,
|
||||
int current, List<T> selected,
|
||||
Function<List<T>, Boolean> callback) {
|
||||
assert n <= all.size();
|
||||
assert m <= n;
|
||||
assert current <= all.size();
|
||||
if (selected == null) {
|
||||
selected = new ArrayList<>(m);
|
||||
}
|
||||
|
||||
if (m == 0) {
|
||||
assert selected.size() > 0 : selected;
|
||||
// All n items are selected
|
||||
List<T> tmpResult = Collections.unmodifiableList(selected);
|
||||
return callback.apply(tmpResult);
|
||||
}
|
||||
if (n == m) {
|
||||
// No choice, select all n items, we don't update the `result` here
|
||||
List<T> tmpResult = new ArrayList<>(selected);
|
||||
tmpResult.addAll(all.subList(current, all.size()));
|
||||
return callback.apply(tmpResult);
|
||||
}
|
||||
|
||||
if (current >= all.size()) {
|
||||
// Reach the end of items
|
||||
return false;
|
||||
}
|
||||
|
||||
// Select current item, continue to select C(m-1, n-1)
|
||||
int index = selected.size();
|
||||
selected.add(all.get(current));
|
||||
++current;
|
||||
if (cnm(all, n - 1, m - 1, current, selected, callback)) {
|
||||
// NOTE: we can pop the tailing items if want to keep result clear
|
||||
return true;
|
||||
}
|
||||
// Not select current item, pop it and continue to select C(m-1, n)
|
||||
selected.remove(index);
|
||||
assert selected.size() == index : selected;
|
||||
return cnm(all, n - 1, m, current, selected, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse A(n, m) combinations of a list with n = m = all.size()
|
||||
* @param all list to contain all items for combination
|
||||
* @return List<List<T>> all combinations
|
||||
*/
|
||||
public static <T> List<List<T>> anm(List<T> all) {
|
||||
return anm(all, all.size(), all.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse A(n, m) combinations of a list
|
||||
* @param all list to contain all items for combination
|
||||
* @param n m of A(n, m)
|
||||
* @param m n of A(n, m)
|
||||
* @return List<List<T>> all combinations
|
||||
*/
|
||||
public static <T> List<List<T>> anm(List<T> all, int n, int m) {
|
||||
List<List<T>> combs = new ArrayList<>();
|
||||
anm(all, n, m, comb -> {
|
||||
combs.add(comb);
|
||||
return false;
|
||||
});
|
||||
return combs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse A(n, m) combinations of a list to find first matched
|
||||
* result combination and call back with the result.
|
||||
* @param all list to contain all items for combination
|
||||
* @param n m of A(n, m)
|
||||
* @param m n of A(n, m)
|
||||
* @return true if matched any kind of items combination else false, the
|
||||
* callback can always return false if you want to traverse all combinations
|
||||
*/
|
||||
public static <T> boolean anm(List<T> all, int n, int m,
|
||||
Function<List<T>, Boolean> callback) {
|
||||
return anm(all, n, m, null, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse A(n, m) combinations of a list to find first matched
|
||||
* result combination and call back with the result.
|
||||
* @param all list to contain all items for combination
|
||||
* @param n m of A(n, m)
|
||||
* @param m n of A(n, m)
|
||||
* @param selected list to contain selected items
|
||||
* @return true if matched any kind of items combination else false, the
|
||||
* callback can always return false if you want to traverse all combinations
|
||||
*/
|
||||
private static <T> boolean anm(List<T> all, int n, int m,
|
||||
List<Integer> selected,
|
||||
Function<List<T>, Boolean> callback) {
|
||||
assert n <= all.size();
|
||||
assert m <= n;
|
||||
if (selected == null) {
|
||||
selected = new ArrayList<>(m);
|
||||
}
|
||||
|
||||
if (m == 0) {
|
||||
// All n items are selected
|
||||
List<T> tmpResult = new ArrayList<>();
|
||||
for (int i : selected) {
|
||||
tmpResult.add(all.get(i));
|
||||
}
|
||||
return callback.apply(tmpResult);
|
||||
}
|
||||
|
||||
for (int i = 0; i < all.size(); i++) {
|
||||
if (selected.contains(i)) {
|
||||
continue;
|
||||
}
|
||||
int index = selected.size();
|
||||
selected.add(i);
|
||||
|
||||
// Select current item, continue to select A(m-1, n-1)
|
||||
if (anm(all, n - 1, m - 1, selected, callback)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
selected.remove(index);
|
||||
assert selected.size() == index : selected;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.hugegraph.date.SafeDateFormat;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public final class DateUtil {
|
||||
|
||||
public static final Date DATE_ZERO = new Date(0L);
|
||||
|
||||
private static final Map<String, String> VALID_DFS = ImmutableMap.of(
|
||||
"^\\d{4}-\\d{1,2}-\\d{1,2}",
|
||||
"yyyy-MM-dd",
|
||||
"^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{2}:\\d{2}:\\d{2}",
|
||||
"yyyy-MM-dd HH:mm:ss",
|
||||
"^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{2}:\\d{2}:\\d{2}\\.\\d{1,3}",
|
||||
"yyyy-MM-dd HH:mm:ss.SSS"
|
||||
);
|
||||
|
||||
private static final Map<String, SafeDateFormat> DATE_FORMATS = new ConcurrentHashMap<>();
|
||||
|
||||
public static Date parse(String value) {
|
||||
for (Map.Entry<String, String> entry : VALID_DFS.entrySet()) {
|
||||
if (value.matches(entry.getKey())) {
|
||||
return parse(value, entry.getValue());
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Expected date format is: %s, but got '%s'", VALID_DFS.values(), value));
|
||||
}
|
||||
|
||||
public static Date parse(String value, String df) {
|
||||
SafeDateFormat dateFormat = getDateFormat(df);
|
||||
return dateFormat.parse(value);
|
||||
}
|
||||
|
||||
public static Date now() {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
private static SafeDateFormat getDateFormat(String df) {
|
||||
SafeDateFormat dateFormat = DATE_FORMATS.get(df);
|
||||
if (dateFormat == null) {
|
||||
dateFormat = new SafeDateFormat(df);
|
||||
SafeDateFormat previous = DATE_FORMATS.putIfAbsent(df, dateFormat);
|
||||
if (previous != null) {
|
||||
dateFormat = previous;
|
||||
}
|
||||
}
|
||||
return dateFormat;
|
||||
}
|
||||
|
||||
public static Object toPattern(String df) {
|
||||
SafeDateFormat dateFormat = getDateFormat(df);
|
||||
return dateFormat.toPattern();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
public final class E {
|
||||
|
||||
public static void checkNotNull(Object object, String elem) {
|
||||
Preconditions.checkNotNull(object, "The '%s' can't be null", elem);
|
||||
}
|
||||
|
||||
public static void checkNotNull(Object object, String elem, String owner) {
|
||||
Preconditions.checkNotNull(object,
|
||||
"The '%s' of '%s' can't be null",
|
||||
elem, owner);
|
||||
}
|
||||
|
||||
public static void checkNotEmpty(Collection<?> collection, String elem) {
|
||||
Preconditions.checkArgument(!collection.isEmpty(),
|
||||
"The '%s' can't be empty", elem);
|
||||
}
|
||||
|
||||
public static void checkNotEmpty(Collection<?> collection, String elem, String owner) {
|
||||
Preconditions.checkArgument(!collection.isEmpty(),
|
||||
"The '%s' of '%s' can't be empty",
|
||||
elem, owner);
|
||||
}
|
||||
|
||||
public static void checkArgument(boolean expression,
|
||||
@Nullable String message,
|
||||
@Nullable Object... args) {
|
||||
Preconditions.checkArgument(expression, message, args);
|
||||
}
|
||||
|
||||
public static void checkArgumentNotNull(Object object,
|
||||
@Nullable String message,
|
||||
@Nullable Object... args) {
|
||||
Preconditions.checkArgument(object != null, message, args);
|
||||
}
|
||||
|
||||
public static void checkState(boolean expression,
|
||||
@Nullable String message,
|
||||
@Nullable Object... args) {
|
||||
Preconditions.checkState(expression, message, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public final class ExceptionUtil {
|
||||
|
||||
public static Throwable rootCause(Throwable e) {
|
||||
Throwable cause = e;
|
||||
while (cause.getCause() != null) {
|
||||
cause = cause.getCause();
|
||||
}
|
||||
return cause;
|
||||
}
|
||||
|
||||
public static RuntimeException transToRuntimeException(Throwable e) {
|
||||
if (e instanceof RuntimeException) {
|
||||
return (RuntimeException) e;
|
||||
}
|
||||
return new RuntimeException(rootCause(e).getMessage(), e);
|
||||
}
|
||||
|
||||
public static <T> T futureGet(Future<T> future) {
|
||||
try {
|
||||
return future.get();
|
||||
} catch (InterruptedException e) {
|
||||
throw ExceptionUtil.transToRuntimeException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw ExceptionUtil.transToRuntimeException(e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
import org.apache.hugegraph.concurrent.PausableScheduledThreadPool;
|
||||
|
||||
|
||||
|
||||
public final class ExecutorUtil {
|
||||
|
||||
public static ThreadPoolExecutor newDynamicThreadExecutor(String name,
|
||||
int corePoolSize,
|
||||
int maximumPoolSize) {
|
||||
|
||||
long keepAliveTime = 60L;
|
||||
TimeUnit unit = TimeUnit.SECONDS;
|
||||
ThreadFactory factory = new BasicThreadFactory.Builder()
|
||||
.namingPattern(name)
|
||||
.build();
|
||||
CustomBlockingQueue<Runnable> workQueue = new CustomBlockingQueue<>();
|
||||
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
|
||||
corePoolSize,
|
||||
maximumPoolSize,
|
||||
keepAliveTime,
|
||||
unit,
|
||||
workQueue,
|
||||
factory,
|
||||
new ThreadPoolExecutor.CallerRunsPolicy()
|
||||
);
|
||||
|
||||
workQueue.setThreadPoolExecutor(threadPoolExecutor);
|
||||
|
||||
return threadPoolExecutor;
|
||||
}
|
||||
|
||||
static class CustomBlockingQueue<E> extends LinkedBlockingQueue<E> {
|
||||
private ThreadPoolExecutor threadPoolExecutor;
|
||||
|
||||
public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) {
|
||||
this.threadPoolExecutor = threadPoolExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(E e) {
|
||||
if (threadPoolExecutor.getPoolSize() < threadPoolExecutor.getMaximumPoolSize()) {
|
||||
return false;
|
||||
}
|
||||
return super.offer(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ExecutorService newFixedThreadPool(String name) {
|
||||
return newFixedThreadPool(1, name);
|
||||
}
|
||||
|
||||
public static ExecutorService newFixedThreadPool(int size, String name) {
|
||||
ThreadFactory factory = new BasicThreadFactory.Builder()
|
||||
.namingPattern(name)
|
||||
.build();
|
||||
return Executors.newFixedThreadPool(size, factory);
|
||||
}
|
||||
|
||||
public static ScheduledExecutorService newScheduledThreadPool(String name) {
|
||||
return newScheduledThreadPool(1, name);
|
||||
}
|
||||
|
||||
public static ScheduledExecutorService newScheduledThreadPool(int size,
|
||||
String name) {
|
||||
ThreadFactory factory = new BasicThreadFactory.Builder()
|
||||
.namingPattern(name)
|
||||
.build();
|
||||
return Executors.newScheduledThreadPool(size, factory);
|
||||
}
|
||||
|
||||
public static PausableScheduledThreadPool newPausableScheduledThreadPool(
|
||||
String name) {
|
||||
return newPausableScheduledThreadPool(1, name);
|
||||
}
|
||||
|
||||
public static PausableScheduledThreadPool newPausableScheduledThreadPool(
|
||||
int size, String name) {
|
||||
ThreadFactory factory = new BasicThreadFactory.Builder()
|
||||
.namingPattern(name)
|
||||
.build();
|
||||
return new PausableScheduledThreadPool(size, factory);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.hash.Hashing;
|
||||
|
||||
public final class HashUtil {
|
||||
|
||||
private static final Charset CHARSET = Charsets.UTF_8;
|
||||
|
||||
public static byte[] hash(byte[] bytes) {
|
||||
return Hashing.murmur3_32().hashBytes(bytes).asBytes();
|
||||
}
|
||||
|
||||
public static String hash(String value) {
|
||||
return Hashing.murmur3_32().hashString(value, CHARSET).toString();
|
||||
}
|
||||
|
||||
public static byte[] hash128(byte[] bytes) {
|
||||
return Hashing.murmur3_128().hashBytes(bytes).asBytes();
|
||||
}
|
||||
|
||||
public static String hash128(String value) {
|
||||
return Hashing.murmur3_128().hashString(value, CHARSET).toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public final class InsertionOrderUtil {
|
||||
|
||||
public static <K, V> Map<K, V> newMap() {
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> newMap(int initialCapacity) {
|
||||
return new LinkedHashMap<>(initialCapacity);
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> newMap(Map<K, V> origin) {
|
||||
return new LinkedHashMap<>(origin);
|
||||
}
|
||||
|
||||
public static <V> Set<V> newSet() {
|
||||
return new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
public static <V> Set<V> newSet(int initialCapacity) {
|
||||
return new LinkedHashSet<>(initialCapacity);
|
||||
}
|
||||
|
||||
public static <V> Set<V> newSet(Set<V> origin) {
|
||||
return new LinkedHashSet<>(origin);
|
||||
}
|
||||
|
||||
public static <V> List<V> newList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public static <V> List<V> newList(int initialCapacity) {
|
||||
return new ArrayList<>(initialCapacity);
|
||||
}
|
||||
|
||||
public static <V> List<V> newList(List<V> origin) {
|
||||
return new ArrayList<>(origin);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.hugegraph.rest.SerializeException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Utility class for JSON operations.
|
||||
*/
|
||||
public final class JsonUtilCommon {
|
||||
|
||||
/**
|
||||
* ObjectMapper instance used for JSON operations.
|
||||
*/
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* Registers a module with the ObjectMapper.
|
||||
*
|
||||
* @param module the module to register
|
||||
*/
|
||||
public static void registerModule(Module module) {
|
||||
MAPPER.registerModule(module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object to a JSON string.
|
||||
*
|
||||
* @param object the object to convert
|
||||
* @return the JSON string representation of the object
|
||||
* @throws SerializeException if the object cannot be serialized
|
||||
*/
|
||||
public static String toJson(Object object) {
|
||||
try {
|
||||
return MAPPER.writeValueAsString(object);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new SerializeException("Failed to serialize object '%s'", e, object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a JSON string to an object of the specified class.
|
||||
*
|
||||
* @param json the JSON string
|
||||
* @param clazz the class of the object
|
||||
* @return the object represented by the JSON string
|
||||
* @throws SerializeException if the JSON string cannot be deserialized
|
||||
*/
|
||||
public static <T> T fromJson(String json, Class<T> clazz) {
|
||||
try {
|
||||
return MAPPER.readValue(json, clazz);
|
||||
} catch (IOException e) {
|
||||
throw new SerializeException("Failed to deserialize json '%s'", e, json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a JsonNode to an object of the specified class.
|
||||
*
|
||||
* @param node the JsonNode
|
||||
* @param clazz the class of the object
|
||||
* @return the object represented by the JsonNode
|
||||
* @throws SerializeException if the JsonNode cannot be deserialized
|
||||
*/
|
||||
public static <T> T convertValue(JsonNode node, Class<T> clazz) {
|
||||
try {
|
||||
return MAPPER.convertValue(node, clazz);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new SerializeException("Failed to deserialize json node '%s'", e, node);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class Log {
|
||||
|
||||
public static Logger logger(String name) {
|
||||
return LoggerFactory.getLogger(name);
|
||||
}
|
||||
|
||||
public static Logger logger(Class<?> clazz) {
|
||||
return LoggerFactory.getLogger(clazz);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
public final class LongEncoding {
|
||||
|
||||
private static final String B64_SYMBOLS =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
|
||||
|
||||
private static final String LENGTH_SYMBOLS = "0123456789ABCDEF";
|
||||
private static final char SORTABLE_NEG = LENGTH_SYMBOLS.charAt(0);
|
||||
private static final char SIGNED_NEG = '-';
|
||||
|
||||
private static final long FULL_LONG = Long.MIN_VALUE;
|
||||
|
||||
public static String encodeNumber(Object number) {
|
||||
Number num = NumericUtil.convertToNumber(number);
|
||||
long value = NumericUtil.numberToSortableLong(num);
|
||||
return encodeSortable(value);
|
||||
}
|
||||
|
||||
public static Number decodeNumber(String str, Class<?> clazz) {
|
||||
long value = decodeSortable(str);
|
||||
return NumericUtil.sortableLongToNumber(value, clazz);
|
||||
}
|
||||
|
||||
public static String encodeSortable(long num) {
|
||||
boolean negative = false;
|
||||
if (num < 0L) {
|
||||
negative = true;
|
||||
num += FULL_LONG;
|
||||
}
|
||||
|
||||
String encoded = encode(num, B64_SYMBOLS);
|
||||
int length = encoded.length();
|
||||
E.checkArgument(length <= LENGTH_SYMBOLS.length(),
|
||||
"Length symbols can't represent encoded number '%s'",
|
||||
encoded);
|
||||
StringBuilder sb = new StringBuilder(length + 2);
|
||||
if (negative) {
|
||||
sb.append(SORTABLE_NEG);
|
||||
}
|
||||
char len = LENGTH_SYMBOLS.charAt(length);
|
||||
sb.append(len);
|
||||
sb.append(encoded);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static long decodeSortable(String str) {
|
||||
E.checkArgument(str.length() >= 2,
|
||||
"Length of sortable encoded string must be >=2");
|
||||
boolean negative = str.charAt(0) == SORTABLE_NEG;
|
||||
int lengthPos = 0;
|
||||
if (negative) {
|
||||
lengthPos = 1;
|
||||
}
|
||||
int length = B64_SYMBOLS.indexOf(str.charAt(lengthPos));
|
||||
E.checkArgument(length == str.length() - lengthPos - 1,
|
||||
"Can't decode illegal string '%s' with wrong length",
|
||||
str);
|
||||
String encoded = str.substring(lengthPos + 1);
|
||||
long value = decode(encoded, B64_SYMBOLS);
|
||||
if (negative) {
|
||||
value -= FULL_LONG;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static String encodeSignedB64(long value) {
|
||||
boolean negative = false;
|
||||
if (value < 0L) {
|
||||
negative = true;
|
||||
if (value == FULL_LONG) {
|
||||
return "-80000000000";
|
||||
}
|
||||
value = -value;
|
||||
}
|
||||
assert value >= 0L : value;
|
||||
String encoded = encodeB64(value);
|
||||
return negative ? SIGNED_NEG + encoded : encoded;
|
||||
}
|
||||
|
||||
public static long decodeSignedB64(String value) {
|
||||
boolean negative = false;
|
||||
if (!value.isEmpty() && value.charAt(0) == SIGNED_NEG) {
|
||||
negative = true;
|
||||
value = value.substring(1);
|
||||
}
|
||||
long decoded = decodeB64(value);
|
||||
return negative ? -decoded : decoded;
|
||||
}
|
||||
|
||||
public static boolean validB64Char(char c) {
|
||||
return B64_SYMBOLS.indexOf(c) != -1;
|
||||
}
|
||||
|
||||
public static String encodeB64(long num) {
|
||||
return encode(num, B64_SYMBOLS);
|
||||
}
|
||||
|
||||
public static long decodeB64(String str) {
|
||||
return decode(str, B64_SYMBOLS);
|
||||
}
|
||||
|
||||
public static long decode(String encoded, String symbols) {
|
||||
final int B = symbols.length();
|
||||
E.checkArgument(B > 0, "The symbols parameter can't be empty");
|
||||
long num = 0L;
|
||||
for (char ch : encoded.toCharArray()) {
|
||||
num *= B;
|
||||
int pos = symbols.indexOf(ch);
|
||||
if (pos < 0) {
|
||||
throw new NumberFormatException(String.format(
|
||||
"Can't decode symbol '%s' in string '%s'",
|
||||
ch, encoded));
|
||||
}
|
||||
num += pos;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
public static String encode(long num, String symbols) {
|
||||
final int B = symbols.length();
|
||||
E.checkArgument(num >= 0L, "Expected non-negative number: %s", num);
|
||||
E.checkArgument(B > 0, "The symbols parameter can't be empty");
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
do {
|
||||
sb.append(symbols.charAt((int) (num % B)));
|
||||
num /= B;
|
||||
} while (num != 0L);
|
||||
|
||||
return sb.reverse().toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Date;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* This file is copied verbatim from Apache Lucene NumericUtils.java
|
||||
* Only the double/float to sortable long/int conversions are retained.
|
||||
*/
|
||||
public final class NumericUtil {
|
||||
|
||||
private static final long FULL_LONG = Long.MIN_VALUE;
|
||||
private static final int FULL_INT = Integer.MIN_VALUE;
|
||||
private static final byte FULL_BYTE = Byte.MIN_VALUE;
|
||||
|
||||
private NumericUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a <code>double</code> value to a sortable signed
|
||||
* <code>long</code>. The value is converted by getting their IEEE 754
|
||||
* floating-point "double format" bit layout and then some bits
|
||||
* are swapped, to be able to compare the result as long. By this the
|
||||
* precision is not reduced, but the value can be easily used as a long. The
|
||||
* sort order (including {@link Double#NaN}) is defined by
|
||||
* {@link Double#compareTo}; {@code NaN} is greater than positive infinity.
|
||||
* @param value input double value
|
||||
* @return output sortable long value
|
||||
* @see #sortableLongToDouble
|
||||
*/
|
||||
public static long doubleToSortableLong(double value) {
|
||||
return sortableDoubleBits(Double.doubleToLongBits(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a sortable <code>long</code> back to a <code>double</code>.
|
||||
* @param value input double value
|
||||
* @return output sortable long value
|
||||
* @see #doubleToSortableLong
|
||||
*/
|
||||
public static double sortableLongToDouble(long value) {
|
||||
return Double.longBitsToDouble(sortableDoubleBits(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a <code>float</code> value to a sortable signed
|
||||
* <code>int</code>. The value is converted by getting their IEEE 754
|
||||
* floating-point "float format" bit layout and then some bits are
|
||||
* swapped, to be able to compare the result as int. By this the precision
|
||||
* is not reduced, but the value can be easily used as an int. The sort order
|
||||
* (including {@link Float#NaN}) is defined by {@link Float#compareTo};
|
||||
* {@code NaN} is greater than positive infinity.
|
||||
* @param value input float value
|
||||
* @return output sortable int value
|
||||
* @see #sortableIntToFloat
|
||||
*/
|
||||
public static int floatToSortableInt(float value) {
|
||||
return sortableFloatBits(Float.floatToIntBits(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a sortable <code>int</code> back to a <code>float</code>.
|
||||
* @param value input int value
|
||||
* @return output sortable float value
|
||||
* @see #floatToSortableInt
|
||||
*/
|
||||
public static float sortableIntToFloat(int value) {
|
||||
return Float.intBitsToFloat(sortableFloatBits(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts IEEE 754 representation of a double to sortable order (or back
|
||||
* to the original)
|
||||
* @param bits The long format of a double value
|
||||
* @return The sortable long value
|
||||
*/
|
||||
public static long sortableDoubleBits(long bits) {
|
||||
return bits ^ ((bits >> 63) & 0x7fffffffffffffffL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts IEEE 754 representation of a float to sortable order (or back to
|
||||
* the original)
|
||||
* @param bits The int format of a float value
|
||||
* @return The sortable int value
|
||||
*/
|
||||
public static int sortableFloatBits(int bits) {
|
||||
/*
|
||||
* Convert to its inverse digits if negative else keep the origin
|
||||
* NOTE: (bits >> 31) is 0x00000000 if bits >= 0
|
||||
* (bits >> 31) is 0xFFFFFFFF if bits < 0
|
||||
*/
|
||||
return bits ^ ((bits >> 31) & 0x7fffffff);
|
||||
}
|
||||
|
||||
public static long numberToSortableLong(Number number) {
|
||||
if (number instanceof Double) {
|
||||
return doubleToSortableLong(number.doubleValue());
|
||||
} else if (number instanceof Float) {
|
||||
return floatToSortableInt(number.floatValue());
|
||||
} else if (number instanceof Long || number instanceof Integer ||
|
||||
number instanceof Short || number instanceof Byte) {
|
||||
return number.longValue();
|
||||
} else if (number instanceof BigDecimal) {
|
||||
BigDecimal bd = (BigDecimal) number;
|
||||
boolean intNumber = bd.stripTrailingZeros().scale() <= 0;
|
||||
return intNumber ? bd.longValueExact() :
|
||||
doubleToSortableLong(bd.doubleValue());
|
||||
}
|
||||
|
||||
// TODO: support other number types
|
||||
throw unsupportedNumberType(number);
|
||||
}
|
||||
|
||||
public static Number sortableLongToNumber(long value, Class<?> clazz) {
|
||||
assert NumericUtil.isNumber(clazz);
|
||||
|
||||
if (clazz == Double.class) {
|
||||
return sortableLongToDouble(value);
|
||||
} else if (clazz == Float.class) {
|
||||
return sortableIntToFloat((int) value);
|
||||
} else if (clazz == Long.class) {
|
||||
return value;
|
||||
} else if (clazz == Integer.class) {
|
||||
return (int) value;
|
||||
} else if (clazz == Short.class) {
|
||||
return (short) value;
|
||||
} else if (clazz == Byte.class) {
|
||||
return (byte) value;
|
||||
}
|
||||
|
||||
// TODO: support other number types
|
||||
throw unsupportedNumberType(clazz);
|
||||
}
|
||||
|
||||
public static byte[] numberToSortableBytes(Number number) {
|
||||
if (number instanceof Long) {
|
||||
return longToSortableBytes(number.longValue());
|
||||
} else if (number instanceof Double) {
|
||||
long value = doubleToSortableLong(number.doubleValue());
|
||||
return longToSortableBytes(value);
|
||||
} else if (number instanceof Float) {
|
||||
int value = floatToSortableInt(number.floatValue());
|
||||
return intToSortableBytes(value);
|
||||
} else if (number instanceof Integer || number instanceof Short) {
|
||||
return intToSortableBytes(number.intValue());
|
||||
} else if (number instanceof Byte) {
|
||||
return byteToSortableBytes(number.byteValue());
|
||||
}
|
||||
|
||||
// TODO: support other number types
|
||||
throw unsupportedNumberType(number);
|
||||
}
|
||||
|
||||
public static Number sortableBytesToNumber(byte[] bytes, Class<?> clazz) {
|
||||
assert NumericUtil.isNumber(clazz);
|
||||
|
||||
if (clazz == Long.class) {
|
||||
return sortableBytesToLong(bytes);
|
||||
} else if (clazz == Double.class) {
|
||||
return sortableLongToDouble(sortableBytesToLong(bytes));
|
||||
} else if (clazz == Float.class) {
|
||||
return sortableIntToFloat(sortableBytesToInt(bytes));
|
||||
} else if (clazz == Integer.class) {
|
||||
return sortableBytesToInt(bytes);
|
||||
} else if (clazz == Short.class) {
|
||||
return (short) sortableBytesToInt(bytes);
|
||||
} else if (clazz == Byte.class) {
|
||||
return sortableBytesToByte(bytes);
|
||||
}
|
||||
|
||||
// TODO: support other number types
|
||||
throw unsupportedNumberType(clazz);
|
||||
}
|
||||
|
||||
public static Number minValueOf(Class<?> clazz) {
|
||||
E.checkArgumentNotNull(clazz, "The clazz can't be null");
|
||||
|
||||
if (Long.class.isAssignableFrom(clazz) ||
|
||||
Double.class.isAssignableFrom(clazz)) {
|
||||
return Long.MIN_VALUE;
|
||||
}
|
||||
if (Integer.class.isAssignableFrom(clazz) ||
|
||||
Short.class.isAssignableFrom(clazz) ||
|
||||
Float.class.isAssignableFrom(clazz)) {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
if (Byte.class.isAssignableFrom(clazz)) {
|
||||
return Byte.MIN_VALUE;
|
||||
}
|
||||
|
||||
// TODO: support other number types
|
||||
throw unsupportedNumberType(clazz);
|
||||
}
|
||||
|
||||
public static Number maxValueOf(Class<?> clazz) {
|
||||
E.checkArgumentNotNull(clazz, "The clazz can't be null");
|
||||
|
||||
if (Long.class.isAssignableFrom(clazz) ||
|
||||
Double.class.isAssignableFrom(clazz)) {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
if (Integer.class.isAssignableFrom(clazz) ||
|
||||
Float.class.isAssignableFrom(clazz) ||
|
||||
Short.class.isAssignableFrom(clazz)) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
if (Byte.class.isAssignableFrom(clazz)) {
|
||||
return Byte.MAX_VALUE;
|
||||
}
|
||||
|
||||
// TODO: support other number types
|
||||
throw unsupportedNumberType(clazz);
|
||||
}
|
||||
|
||||
public static byte[] longToSortableBytes(long value) {
|
||||
return longToBytes(value + FULL_LONG);
|
||||
}
|
||||
|
||||
public static long sortableBytesToLong(byte[] bytes) {
|
||||
return bytesToLong(bytes) - FULL_LONG;
|
||||
}
|
||||
|
||||
public static byte[] intToSortableBytes(int value) {
|
||||
return intToBytes(value + FULL_INT);
|
||||
}
|
||||
|
||||
public static int sortableBytesToInt(byte[] bytes) {
|
||||
return bytesToInt(bytes) - FULL_INT;
|
||||
}
|
||||
|
||||
public static byte[] byteToSortableBytes(byte value) {
|
||||
value += FULL_BYTE;
|
||||
return new byte[]{value};
|
||||
}
|
||||
|
||||
public static byte sortableBytesToByte(byte[] bytes) {
|
||||
assert bytes.length == 1;
|
||||
byte value = bytes[0];
|
||||
value -= FULL_BYTE;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static byte[] longToBytes(long value) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
|
||||
buffer.putLong(value);
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
public static long bytesToLong(byte[] bytes) {
|
||||
assert bytes.length == Long.BYTES;
|
||||
return ByteBuffer.wrap(bytes).getLong();
|
||||
}
|
||||
|
||||
public static byte[] intToBytes(int value) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
|
||||
buffer.putInt(value);
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
public static int bytesToInt(byte[] bytes) {
|
||||
assert bytes.length == Integer.BYTES;
|
||||
return ByteBuffer.wrap(bytes).getInt();
|
||||
}
|
||||
|
||||
public static boolean isNumber(Object value) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
return isNumber(value.getClass());
|
||||
}
|
||||
|
||||
public static boolean isNumber(Class<?> clazz) {
|
||||
if (clazz.isPrimitive()) {
|
||||
return clazz == int.class || clazz == long.class ||
|
||||
clazz == float.class || clazz == double.class ||
|
||||
clazz == short.class || clazz == byte.class;
|
||||
}
|
||||
return Number.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
public static Number convertToNumber(Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Number number;
|
||||
if (isNumber(value)) {
|
||||
number = (Number) value;
|
||||
} else {
|
||||
if (value instanceof Date) {
|
||||
number = ((Date) value).getTime();
|
||||
} else {
|
||||
// TODO: add some more types to convert
|
||||
number = new BigDecimal(value.toString());
|
||||
}
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare object with a number, the object should be a number,
|
||||
* or it can be converted to a BigDecimal
|
||||
* @param first might be number or string
|
||||
* @param second must be number
|
||||
* @return 0 if first is numerically equal to second;
|
||||
* a negative int if first is numerically less than second;
|
||||
* a positive int if first is numerically greater than second.
|
||||
*/
|
||||
public static int compareNumber(Object first, Number second) {
|
||||
if (first == null) {
|
||||
E.checkArgument(first != null,
|
||||
"The first parameter can't be null");
|
||||
}
|
||||
if (second == null) {
|
||||
E.checkArgument(second != null,
|
||||
"The second parameter can't be null");
|
||||
}
|
||||
|
||||
if (first instanceof Number && first instanceof Comparable &&
|
||||
first.getClass().equals(second.getClass())) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Comparable<Number> cmpFirst = (Comparable<Number>) first;
|
||||
return cmpFirst.compareTo(second);
|
||||
}
|
||||
|
||||
Function<Object, BigDecimal> toBig = (number) -> {
|
||||
try {
|
||||
return new BigDecimal(number.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Can't compare between '%s' and '%s', " +
|
||||
"they must be numbers", first, second));
|
||||
}
|
||||
};
|
||||
|
||||
BigDecimal n1 = toBig.apply(first);
|
||||
BigDecimal n2 = toBig.apply(second);
|
||||
|
||||
return n1.compareTo(n2);
|
||||
}
|
||||
|
||||
private static IllegalArgumentException unsupportedNumberType(Class<?> c) {
|
||||
return new IllegalArgumentException(String.format(
|
||||
"Unsupported number type: %s", c.getSimpleName()));
|
||||
}
|
||||
|
||||
private static IllegalArgumentException unsupportedNumberType(Number num) {
|
||||
return new IllegalArgumentException(String.format(
|
||||
"Unsupported number type: %s(%s)",
|
||||
num.getClass().getSimpleName(), num));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.collect.Ordering;
|
||||
|
||||
public class OrderLimitMap<K extends Comparable<K>, V extends Comparable<V>> extends TreeMap<K, V> {
|
||||
|
||||
private static final long serialVersionUID = 756490437953358633L;
|
||||
|
||||
private final int capacity;
|
||||
private final Map<K, V> valueMap;
|
||||
|
||||
private static <V extends Comparable<V>> Ordering<? super V> incr() {
|
||||
return Ordering.from(Comparable::compareTo);
|
||||
}
|
||||
|
||||
private static <V extends Comparable<V>> Ordering<? super V> decr() {
|
||||
return Ordering.from((V o1, V o2) -> -o1.compareTo(o2));
|
||||
}
|
||||
|
||||
public OrderLimitMap(int capacity) {
|
||||
this(capacity, false);
|
||||
}
|
||||
|
||||
public OrderLimitMap(int capacity, boolean incr) {
|
||||
this(capacity, incr ? incr() : decr(), new HashMap<>());
|
||||
}
|
||||
|
||||
private OrderLimitMap(int capacity, Ordering<? super V> ordering,
|
||||
HashMap<K, V> valueMap) {
|
||||
/*
|
||||
* onResultOf: for getting the value for the key from value map
|
||||
* compound: keep insertion order
|
||||
*/
|
||||
super(ordering.onResultOf(Functions.forMap(valueMap))
|
||||
.compound(Ordering.natural()));
|
||||
E.checkArgument(capacity > 0, "The capacity must be > 0");
|
||||
this.capacity = capacity;
|
||||
this.valueMap = valueMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K k, V v) {
|
||||
if (this.valueMap.containsKey(k)) {
|
||||
super.remove(k);
|
||||
} else if (this.valueMap.size() >= this.capacity) {
|
||||
K key = super.lastKey();
|
||||
super.remove(key);
|
||||
this.valueMap.remove(key);
|
||||
}
|
||||
this.valueMap.put(k, v);
|
||||
return super.put(k, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
return this.valueMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getOrDefault(Object key, V defaultValue) {
|
||||
return this.valueMap.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return this.valueMap.containsKey(key);
|
||||
}
|
||||
|
||||
public Map<K, V> topN(int n) {
|
||||
E.checkArgument(n > 0, "'N' Must be positive, but got '%s'", n);
|
||||
Map<K, V> top = InsertionOrderUtil.newMap();
|
||||
int i = 0;
|
||||
for (Map.Entry<K, V> entry : this.entrySet()) {
|
||||
top.put(entry.getKey(), entry.getValue());
|
||||
if (++i >= n) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return top;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hugegraph.iterator.ExtendableIterator;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.reflect.ClassPath;
|
||||
import com.google.common.reflect.ClassPath.ClassInfo;
|
||||
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtMethod;
|
||||
import javassist.NotFoundException;
|
||||
|
||||
public final class ReflectionUtil {
|
||||
|
||||
public static boolean isSimpleType(Class<?> type) {
|
||||
return type.isPrimitive() ||
|
||||
type.equals(String.class) ||
|
||||
type.equals(Boolean.class) ||
|
||||
type.equals(Character.class) ||
|
||||
NumericUtil.isNumber(type);
|
||||
}
|
||||
|
||||
public static List<Method> getMethodsAnnotatedWith(
|
||||
Class<?> type,
|
||||
Class<? extends Annotation> annotation,
|
||||
boolean withSuperClass) {
|
||||
final List<Method> methods = new LinkedList<>();
|
||||
Class<?> klass = type;
|
||||
do {
|
||||
for (Method method : klass.getDeclaredMethods()) {
|
||||
if (method.isAnnotationPresent(annotation)) {
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
klass = klass.getSuperclass();
|
||||
} while (klass != Object.class && withSuperClass);
|
||||
return methods;
|
||||
}
|
||||
|
||||
public static List<CtMethod> getMethodsAnnotatedWith(
|
||||
CtClass type,
|
||||
Class<? extends Annotation> annotation,
|
||||
boolean withSuperClass)
|
||||
throws NotFoundException {
|
||||
final List<CtMethod> methods = new LinkedList<>();
|
||||
|
||||
CtClass klass = type;
|
||||
do {
|
||||
for (CtMethod method : klass.getDeclaredMethods()) {
|
||||
if (method.hasAnnotation(annotation)) {
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
klass = klass.getSuperclass();
|
||||
} while (klass != null && withSuperClass);
|
||||
return methods;
|
||||
}
|
||||
|
||||
public static Iterator<ClassInfo> classes(String... packages)
|
||||
throws IOException {
|
||||
ClassPath path = ClassPath.from(ReflectionUtil.class.getClassLoader());
|
||||
ExtendableIterator<ClassInfo> results = new ExtendableIterator<>();
|
||||
for (String p : packages) {
|
||||
results.extend(path.getTopLevelClassesRecursive(p).iterator());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public static List<String> superClasses(String clazz)
|
||||
throws NotFoundException {
|
||||
CtClass klass = ClassPool.getDefault().get(clazz);
|
||||
CtClass parent = klass.getSuperclass();
|
||||
|
||||
List<String> results = new LinkedList<>();
|
||||
while (parent != null) {
|
||||
results.add(parent.getName());
|
||||
parent = parent.getSuperclass();
|
||||
}
|
||||
return Lists.reverse(results);
|
||||
}
|
||||
|
||||
public static List<String> nestedClasses(String clazz)
|
||||
throws NotFoundException {
|
||||
CtClass klass = ClassPool.getDefault().get(clazz);
|
||||
|
||||
List<String> results = new LinkedList<>();
|
||||
for (CtClass nested : klass.getNestedClasses()) {
|
||||
results.add(nested.getName());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public static String packageName(String clazz) {
|
||||
int offset = clazz.lastIndexOf(".");
|
||||
if (offset > 0) {
|
||||
return clazz.substring(0, offset);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
public final class StringUtil {
|
||||
|
||||
public static Chars[] splitToCharsArray(String text, String delimiter) {
|
||||
E.checkArgument(delimiter.length() > 0,
|
||||
"The delimiter can't be empty");
|
||||
Chars[] buffer = new Chars[text.length()];
|
||||
int count = Chars.split(text, delimiter, buffer);
|
||||
if (count == buffer.length) {
|
||||
return buffer;
|
||||
}
|
||||
Chars[] result = new Chars[count];
|
||||
System.arraycopy(buffer, 0, result, 0, count);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String[] split(String text, String delimiter) {
|
||||
E.checkArgument(delimiter.length() > 0,
|
||||
"The delimiter can't be empty");
|
||||
Chars[] buffer = new Chars[text.length()];
|
||||
int count = Chars.split(text, delimiter, buffer);
|
||||
String[] result = new String[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
result[i] = buffer[i].toString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class Chars implements CharSequence {
|
||||
|
||||
private final char[] chars;
|
||||
private final int start;
|
||||
private final int end;
|
||||
|
||||
public Chars(char[] chars, int start, int end) {
|
||||
E.checkArgument(0 < start && start < chars.length || start == 0,
|
||||
"Invalid start parameter %s", start);
|
||||
E.checkArgument(start <= end && end <= chars.length,
|
||||
"Invalid end parameter %s", end);
|
||||
this.chars = chars;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return this.end - this.start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return this.chars[this.start + index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return new Chars(this.chars, this.start + start, this.start + end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (!(object instanceof Chars)) {
|
||||
return false;
|
||||
}
|
||||
Chars other = (Chars) object;
|
||||
return this.toString().equals(other.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.toString().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new String(this.chars, this.start, this.length());
|
||||
}
|
||||
|
||||
public static Chars of(String string) {
|
||||
return new Chars(string.toCharArray(), 0, string.length());
|
||||
}
|
||||
|
||||
public static Chars[] of(String... strings) {
|
||||
Chars[] results = new Chars[strings.length];
|
||||
for (int i = 0; i < strings.length; i++) {
|
||||
results[i] = Chars.of(strings[i]);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public static int split(String text, String delimiter, Chars[] buffer) {
|
||||
int count = 0;
|
||||
int from = 0;
|
||||
char[] chars = text.toCharArray();
|
||||
for (int to; (to = text.indexOf(delimiter, from)) >= 0;
|
||||
from = to + delimiter.length()) {
|
||||
buffer[count++] = new Chars(chars, from, to);
|
||||
}
|
||||
if (from < text.length()) {
|
||||
buffer[count++] = new Chars(chars, from, text.length());
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Date;
|
||||
|
||||
public final class TimeUtil {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static long BASE_TIME = new Date(2017 - 1900, 10, 28).getTime();
|
||||
|
||||
public static long timeGen() {
|
||||
return System.currentTimeMillis() - BASE_TIME;
|
||||
}
|
||||
|
||||
public static long timeGen(Date date) {
|
||||
return date.getTime() - BASE_TIME;
|
||||
}
|
||||
|
||||
public static long timeGen(long time) {
|
||||
return time - BASE_TIME;
|
||||
}
|
||||
|
||||
public static long tillNextMillis(long lastTimestamp) {
|
||||
long timestamp = timeGen();
|
||||
while (timestamp <= lastTimestamp) {
|
||||
timestamp = timeGen();
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public static String readableTime(long time) {
|
||||
if (time > 60 * 1000) {
|
||||
// Remove the milliseconds part
|
||||
time = time / 1000 * 1000;
|
||||
}
|
||||
Duration duration = Duration.ofMillis(time);
|
||||
return duration.toString()
|
||||
.substring(2)
|
||||
.replaceAll("(\\d[HMS])(?!$)", "$1 ")
|
||||
.toLowerCase();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Duration;
|
||||
|
||||
public final class UnitUtil {
|
||||
|
||||
public static double bytesToMB(long bytes) {
|
||||
return doubleWith2Scale(bytes / (double) Bytes.MB);
|
||||
}
|
||||
|
||||
public static double bytesToGB(long bytes) {
|
||||
return doubleWith2Scale(bytes / (double) Bytes.GB);
|
||||
}
|
||||
|
||||
public static double doubleWith2Scale(double value) {
|
||||
BigDecimal decimal = new BigDecimal(value);
|
||||
return decimal.setScale(2, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
public static String bytesToReadableString(long bytes) {
|
||||
// NOTE: FileUtils.byteCountToDisplaySize() lost decimal precision
|
||||
final String[] units = {"B", "KB", "MB", "GB", "TB", "PB", "EB"};
|
||||
if (bytes <= 0L) {
|
||||
return "0 B";
|
||||
}
|
||||
int i = (int) (Math.log(bytes) / Math.log(1024));
|
||||
E.checkArgument(i < units.length,
|
||||
"The bytes parameter is out of %s unit: %s",
|
||||
units[units.length - 1], bytes);
|
||||
double value = bytes / Math.pow(1024, i);
|
||||
if (value % 1L == 0L) {
|
||||
return ((long) value) + " " + units[i];
|
||||
} else {
|
||||
return doubleWith2Scale(value) + " " + units[i];
|
||||
}
|
||||
}
|
||||
|
||||
public static long bytesFromReadableString(String valueWithUnit) {
|
||||
int spacePos = valueWithUnit.indexOf(" ");
|
||||
E.checkArgument(spacePos >= 0,
|
||||
"Invalid readable bytes '%s', " +
|
||||
"expect format like '10 MB'", valueWithUnit);
|
||||
String unit = valueWithUnit.substring(spacePos + 1);
|
||||
|
||||
long factor;
|
||||
switch (unit.trim().toUpperCase()) {
|
||||
case "B":
|
||||
case "BYTE":
|
||||
case "BYTES":
|
||||
factor = 1L;
|
||||
break;
|
||||
case "KB":
|
||||
case "KIB":
|
||||
factor = Bytes.KB;
|
||||
break;
|
||||
case "MB":
|
||||
case "MIB":
|
||||
factor = Bytes.MB;
|
||||
break;
|
||||
case "GB":
|
||||
case "GIB":
|
||||
factor = Bytes.GB;
|
||||
break;
|
||||
case "TB":
|
||||
case "TIB":
|
||||
factor = Bytes.TB;
|
||||
break;
|
||||
case "PB":
|
||||
case "PIB":
|
||||
factor = Bytes.PB;
|
||||
break;
|
||||
case "EB":
|
||||
case "EIB":
|
||||
factor = Bytes.EB;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized unit " + unit);
|
||||
}
|
||||
|
||||
double value;
|
||||
try {
|
||||
value = Double.parseDouble(valueWithUnit.substring(0, spacePos));
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Invalid parameter(not number): '%s'", valueWithUnit), e);
|
||||
}
|
||||
value = value * factor;
|
||||
E.checkArgument(value <= Long.MAX_VALUE,
|
||||
"The value %s from parameter '%s' is out of range",
|
||||
value, valueWithUnit);
|
||||
return (long) value;
|
||||
}
|
||||
|
||||
public static String timestampToReadableString(long time) {
|
||||
Duration duration = Duration.ofMillis(time);
|
||||
long days = duration.toDays();
|
||||
long hours = duration.toHours();
|
||||
long minutes = duration.toMinutes();
|
||||
long seconds = duration.getSeconds();
|
||||
|
||||
if (days > 0) {
|
||||
return String.format("%dd%dh%dm%ds",
|
||||
days,
|
||||
hours % 24,
|
||||
minutes % 60,
|
||||
seconds % 60);
|
||||
} else if (hours > 0) {
|
||||
return String.format("%dh%dm%ds",
|
||||
hours,
|
||||
minutes % 60,
|
||||
seconds % 60);
|
||||
} else if (minutes > 0) {
|
||||
return String.format("%dm%ds",
|
||||
minutes,
|
||||
seconds % 60);
|
||||
} else if (seconds > 0) {
|
||||
long ms = duration.toMillis() % 1000L;
|
||||
if (ms > 0L) {
|
||||
return String.format("%ds%dms", seconds, ms);
|
||||
} else {
|
||||
return String.format("%ds", seconds);
|
||||
}
|
||||
} else {
|
||||
return String.format("%dms", duration.toMillis());
|
||||
}
|
||||
}
|
||||
|
||||
public static long timestampFromReadableString(String valueWithUnit) {
|
||||
long ms = 0L;
|
||||
// Adapt format 'nDnHnMnS' to 'PnYnMnDTnHnMnS'
|
||||
String formatDuration = valueWithUnit.toUpperCase();
|
||||
if (formatDuration.indexOf('D') >= 0) {
|
||||
// Contains days
|
||||
assert !formatDuration.contains("MS");
|
||||
formatDuration = "P" + formatDuration.replace("D", "DT");
|
||||
} else {
|
||||
// Not exists days
|
||||
int msPos = formatDuration.indexOf("MS");
|
||||
// If contains ms, remove the ms part
|
||||
if (msPos >= 0) {
|
||||
int sPos = formatDuration.indexOf("S");
|
||||
if (0 <= sPos && sPos < msPos) {
|
||||
// If contains second part
|
||||
sPos += 1;
|
||||
ms = Long.parseLong(formatDuration.substring(sPos, msPos));
|
||||
ms %= 1000L;
|
||||
formatDuration = formatDuration.substring(0, sPos);
|
||||
} else {
|
||||
// Not contains second part, only exists ms
|
||||
ms = Long.parseLong(formatDuration.substring(0, msPos));
|
||||
return ms;
|
||||
}
|
||||
} else {
|
||||
assert formatDuration.endsWith("S");
|
||||
}
|
||||
formatDuration = "PT" + formatDuration;
|
||||
}
|
||||
|
||||
Duration duration = Duration.parse(formatDuration);
|
||||
return duration.toMillis() + ms;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.Objects;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public final class VersionUtil {
|
||||
|
||||
/**
|
||||
* Compare if a version is inside a range [begin, end)
|
||||
* @param version The version to be compared
|
||||
* @param begin The lower bound of the range
|
||||
* @param end The upper bound of the range
|
||||
* @return true if belong to the range, otherwise false
|
||||
*/
|
||||
public static boolean match(Version version, String begin, String end) {
|
||||
E.checkArgumentNotNull(version, "The version to match is null");
|
||||
return version.compareTo(new Version(begin)) >= 0 &&
|
||||
version.compareTo(new Version(end)) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare if a version is greater than the other one (inclusive)
|
||||
* @param version The version to be compared
|
||||
* @param other The lower bound of the range
|
||||
* @return true if it's greater than the other, otherwise false
|
||||
*/
|
||||
public static boolean gte(String version, String other) {
|
||||
E.checkArgumentNotNull(version, "The version to match is null");
|
||||
return new Version(version).compareTo(new Version(other)) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a component version is matched expected range,
|
||||
* throw an exception if it's not matched.
|
||||
* @param version The version to be checked
|
||||
* @param begin The lower bound of the range
|
||||
* @param end The upper bound of the range
|
||||
* @param component The owner component of version
|
||||
*/
|
||||
public static void check(Version version, String begin, String end,
|
||||
String component) {
|
||||
E.checkState(VersionUtil.match(version, begin, end),
|
||||
"The version %s of '%s' is not in [%s, %s)",
|
||||
version, component, begin, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get implementation version from manifest in jar
|
||||
* @param clazz The class to be load from jar package
|
||||
* @return The implementation version
|
||||
*/
|
||||
public static String getImplementationVersion(Class<?> clazz) {
|
||||
/*
|
||||
* We don't use Package.getImplementationVersion() due to
|
||||
* a duplicate package would override the origin package info.
|
||||
*/
|
||||
String className = clazz.getSimpleName() + ".class";
|
||||
String classPath = Objects.requireNonNull(clazz.getResource(className)).toString();
|
||||
if (!classPath.startsWith("jar:file:")) {
|
||||
// Class not from JAR
|
||||
return null;
|
||||
}
|
||||
int offset = classPath.lastIndexOf("!");
|
||||
assert offset > 0;
|
||||
// Get manifest file path
|
||||
String manifestPath = classPath.substring(0, offset + 1);
|
||||
return getImplementationVersion(manifestPath);
|
||||
}
|
||||
|
||||
public static String getImplementationVersion(String manifestPath) {
|
||||
manifestPath += "/META-INF/MANIFEST.MF";
|
||||
|
||||
Manifest manifest;
|
||||
try {
|
||||
manifest = new Manifest(new URL(manifestPath).openStream());
|
||||
} catch (IOException ignored) {
|
||||
return null;
|
||||
}
|
||||
return manifest.getMainAttributes()
|
||||
.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version from pom.xml
|
||||
* @return The pom version
|
||||
*/
|
||||
public static String getPomVersion() {
|
||||
String cmd = "mvn help:evaluate -Dexpression=project.version " +
|
||||
"-q -DforceStdout";
|
||||
Process process = null;
|
||||
InputStreamReader isr = null;
|
||||
try {
|
||||
process = Runtime.getRuntime().exec(cmd);
|
||||
process.waitFor();
|
||||
|
||||
isr = new InputStreamReader(process.getInputStream());
|
||||
BufferedReader br = new BufferedReader(isr);
|
||||
return br.readLine();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (isr != null) {
|
||||
try {
|
||||
isr.close();
|
||||
} catch (Exception ignored) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy child process
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Version implements Comparable<Version> {
|
||||
|
||||
public static Version of(Class<?> clazz) {
|
||||
return Version.of(clazz, null);
|
||||
}
|
||||
|
||||
public static Version of(Class<?> clazz, String defaultValue) {
|
||||
String v = getImplementationVersion(clazz);
|
||||
if (v == null) {
|
||||
v = defaultValue;
|
||||
}
|
||||
return v == null ? null : new Version(v);
|
||||
}
|
||||
|
||||
public static Version of(String version) {
|
||||
return new Version(version);
|
||||
}
|
||||
|
||||
/************************** Version define **************************/
|
||||
|
||||
private final String version;
|
||||
private final int[] parts;
|
||||
|
||||
public Version(String version) {
|
||||
E.checkArgumentNotNull(version, "The version is null");
|
||||
E.checkArgument(version.matches("[0-9]+(\\.[0-9]+)*"),
|
||||
"Invalid version format: %s", version);
|
||||
this.version = version;
|
||||
this.parts = parseVersion(version);
|
||||
}
|
||||
|
||||
private static int[] parseVersion(String version) {
|
||||
String[] parts = version.split("\\.");
|
||||
int[] partsNumber = new int[parts.length];
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
partsNumber[i] = Integer.parseInt(parts[i]);
|
||||
}
|
||||
return partsNumber;
|
||||
}
|
||||
|
||||
public final String get() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Version that) {
|
||||
if (that == null) {
|
||||
return 1;
|
||||
}
|
||||
int[] thisParts = this.parts;
|
||||
int[] thatParts = that.parts;
|
||||
int length = Math.max(thisParts.length, thatParts.length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
int thisPart = i < thisParts.length ? thisParts[i] : 0;
|
||||
int thatPart = i < thatParts.length ? thatParts[i] : 0;
|
||||
if (thisPart < thatPart) {
|
||||
return -1;
|
||||
}
|
||||
if (thisPart > thatPart) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
if (this == that) {
|
||||
return true;
|
||||
}
|
||||
if (that == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.getClass() != that.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return this.compareTo((Version) that) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
for (int i = this.parts.length - 1; i >= 0; i--) {
|
||||
int part = this.parts[i];
|
||||
if (part == 0 && hash == 0) {
|
||||
continue;
|
||||
}
|
||||
hash = 31 * hash + Integer.hashCode(part);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.version;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.version;
|
||||
|
||||
import org.apache.hugegraph.util.VersionUtil.Version;
|
||||
|
||||
public class CommonVersion {
|
||||
|
||||
public static final String NAME = "hugegraph-common";
|
||||
|
||||
// The second parameter of Version.of() is for all-in-one JAR
|
||||
public static final Version VERSION = Version.of(CommonVersion.class, "1.5.0");
|
||||
}
|
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.testutil;
|
||||
|
||||
import org.apache.hugegraph.unit.BaseUnitTest;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AssertTest extends BaseUnitTest {
|
||||
|
||||
@Test
|
||||
public void testAssertEquals() {
|
||||
Assert.assertEquals((byte) 1, Byte.valueOf("1"));
|
||||
Assert.assertEquals((short) 1, Short.valueOf("1"));
|
||||
Assert.assertEquals('1', Character.valueOf('1'));
|
||||
Assert.assertEquals(1, Integer.valueOf("1"));
|
||||
Assert.assertEquals(1L, Long.valueOf("1"));
|
||||
Assert.assertEquals(1f, Float.valueOf("1"));
|
||||
Assert.assertEquals(1d, Double.valueOf("1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertEqualsWithError() {
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals((byte) 1, "1");
|
||||
}, e -> {
|
||||
Assert.assertContains("expected: java.lang.Byte",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals((short) 1, "1");
|
||||
}, e -> {
|
||||
Assert.assertContains("expected: java.lang.Short",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals('1', "1");
|
||||
}, e -> {
|
||||
Assert.assertContains("expected: java.lang.Character",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1, "1");
|
||||
}, e -> {
|
||||
Assert.assertContains("expected: java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1L, "1");
|
||||
}, e -> {
|
||||
Assert.assertContains("expected: java.lang.Long",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1f, "1.0");
|
||||
}, e -> {
|
||||
Assert.assertContains("expected: java.lang.Float",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1d, "1.0");
|
||||
}, e -> {
|
||||
Assert.assertContains("expected: java.lang.Double",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1f, "1");
|
||||
}, e -> {
|
||||
Assert.assertContains("expected:<1.0> but was:<1>",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1d, "1");
|
||||
}, e -> {
|
||||
Assert.assertContains("expected:<1.0> but was:<1>",
|
||||
e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertEqualsOfIntWithError() {
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1, (Byte) (byte) 1);
|
||||
}, e -> {
|
||||
Assert.assertContains("expected: java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1, (Short) (short) 1);
|
||||
}, e -> {
|
||||
Assert.assertContains("expected: java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1, (Character) '1');
|
||||
}, e -> {
|
||||
Assert.assertContains("expected: java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1, (Long) 1L);
|
||||
}, e -> {
|
||||
Assert.assertContains("expected: java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1, (Float) 1f);
|
||||
}, e -> {
|
||||
Assert.assertContains("expected:<1> but was:<1.0>",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1, (Double) 1d);
|
||||
}, e -> {
|
||||
Assert.assertContains("expected:<1> but was:<1.0>",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1, "1.0");
|
||||
}, e -> {
|
||||
Assert.assertContains("expected:<1> but was:<1.0>",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1, (Byte) (byte) 2);
|
||||
}, e -> {
|
||||
Assert.assertContains("expected:<1> but was:<2>",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertEquals(1, null);
|
||||
}, e -> {
|
||||
Assert.assertContains("expected:<1> but was:<null>",
|
||||
e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertThrows() {
|
||||
Assert.assertThrows(NullPointerException.class, () -> {
|
||||
throw new NullPointerException();
|
||||
});
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
throw new RuntimeException();
|
||||
});
|
||||
|
||||
Throwable exception = Assert.assertThrows(RuntimeException.class, () -> {
|
||||
throw new RuntimeException("fake-error");
|
||||
});
|
||||
Assert.assertInstanceOf(RuntimeException.class, exception);
|
||||
Assert.assertEquals("fake-error", exception.getMessage());
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
throw new RuntimeException("fake-error");
|
||||
}, e -> {
|
||||
Assert.assertEquals("fake-error", e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertThrowsWithTypeError() {
|
||||
try {
|
||||
Assert.assertThrows(NullPointerException.class, () -> {
|
||||
// pass
|
||||
});
|
||||
Assert.fail("Expect error");
|
||||
} catch (AssertionError e) {
|
||||
Assert.assertContains("java.lang.NullPointerException", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
Assert.assertThrows(NullPointerException.class, () -> {
|
||||
throw new RuntimeException();
|
||||
});
|
||||
Assert.fail("Expect error");
|
||||
} catch (AssertionError e) {
|
||||
Assert.assertContains("java.lang.NullPointerException", e.getMessage());
|
||||
Assert.assertContains("java.lang.RuntimeException", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertThrowsWithMessageError() {
|
||||
try {
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
throw new RuntimeException("fake-error");
|
||||
}, e -> {
|
||||
Assert.assertEquals("fake-error-typo", e.getMessage());
|
||||
});
|
||||
Assert.fail("Expect error");
|
||||
} catch (AssertionError e) {
|
||||
Assert.assertContains("expected:<fake-error[-typo]> but was:<fake-error[]>",
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertGt() {
|
||||
Assert.assertGt((byte) 1, Byte.valueOf("2"));
|
||||
Assert.assertGt((short) 1, Short.valueOf("2"));
|
||||
Assert.assertGt(1, Integer.valueOf("2"));
|
||||
Assert.assertGt(1L, Long.valueOf("2"));
|
||||
Assert.assertGt(1f, Float.valueOf("1.01"));
|
||||
Assert.assertGt(1d, Double.valueOf("1.01"));
|
||||
|
||||
Assert.assertGt((byte) 1, (byte) 2);
|
||||
Assert.assertGt((short) 1, (short) 2);
|
||||
Assert.assertGt(1, 2);
|
||||
Assert.assertGt(1L, 2L);
|
||||
Assert.assertGt(1f, 1.01f);
|
||||
Assert.assertGt(1d, 1.01d);
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGt(1, 0);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: a number > 1", e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGt(1, null);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGt(1, (byte) 2);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGt(1, 1.1);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGt(1, '2');
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGt(0.9, 1);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Double",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGt(0.9d, 0.98f);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Double",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGt(0.9f, 0.98d);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Float",
|
||||
e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertGte() {
|
||||
Assert.assertGte((byte) 1, Byte.valueOf("2"));
|
||||
Assert.assertGte((short) 1, Short.valueOf("2"));
|
||||
Assert.assertGte(1, Integer.valueOf("2"));
|
||||
Assert.assertGte(1L, Long.valueOf("2"));
|
||||
Assert.assertGte(1f, Float.valueOf("1.01"));
|
||||
Assert.assertGte(1d, Double.valueOf("1.01"));
|
||||
|
||||
Assert.assertGte((byte) 1, Byte.valueOf("1"));
|
||||
Assert.assertGte((short) 1, Short.valueOf("1"));
|
||||
Assert.assertGte(1, Integer.valueOf("1"));
|
||||
Assert.assertGte(1L, Long.valueOf("1"));
|
||||
Assert.assertGte(1f, Float.valueOf("1"));
|
||||
Assert.assertGte(1d, Double.valueOf("1"));
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGte(1, 0);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: a number >= 1", e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGte(1, 1.1);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGte(1, '2');
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertLt() {
|
||||
Assert.assertLt((byte) 1, Byte.valueOf("0"));
|
||||
Assert.assertLt((short) 1, Short.valueOf("0"));
|
||||
Assert.assertLt(1, Integer.valueOf("0"));
|
||||
Assert.assertLt(1L, Long.valueOf("0"));
|
||||
Assert.assertLt(1f, Float.valueOf("0.99"));
|
||||
Assert.assertLt(1d, Double.valueOf("0.99"));
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertLt(1, 2);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: a number < 1", e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGt(1, 0.9);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertGt(1, '0');
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertLte() {
|
||||
Assert.assertLte((byte) 1, Byte.valueOf("0"));
|
||||
Assert.assertLte((short) 1, Short.valueOf("0"));
|
||||
Assert.assertLte(1, Integer.valueOf("0"));
|
||||
Assert.assertLte(1L, Long.valueOf("0"));
|
||||
Assert.assertLte(1f, Float.valueOf("0.99"));
|
||||
Assert.assertLte(1d, Double.valueOf("0.99"));
|
||||
|
||||
Assert.assertLte((byte) 1, Byte.valueOf("1"));
|
||||
Assert.assertLte((short) 1, Short.valueOf("1"));
|
||||
Assert.assertLte(1, Integer.valueOf("1"));
|
||||
Assert.assertLte(1L, Long.valueOf("1"));
|
||||
Assert.assertLte(1f, Float.valueOf("1"));
|
||||
Assert.assertLte(1d, Double.valueOf("1"));
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertLte(1, 2);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: a number <= 1", e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertLte(1, 0.9);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertLte(1, '0');
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Integer",
|
||||
e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertContains() {
|
||||
Assert.assertContains("test", "test");
|
||||
Assert.assertContains("test", "hellotest");
|
||||
Assert.assertContains("test", "test123");
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertContains("test123", "test");
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: a string containing",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertContains("null", null);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: a string containing",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(NullPointerException.class, () -> {
|
||||
Assert.assertContains(null, "null");
|
||||
}, e -> {
|
||||
Assert.assertNull(e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssertInstanceOf() {
|
||||
Assert.assertInstanceOf(Integer.class, 1);
|
||||
Assert.assertInstanceOf(Double.class, 1.0);
|
||||
Assert.assertInstanceOf(String.class, "1.0");
|
||||
Assert.assertInstanceOf(BaseUnitTest.class, this);
|
||||
|
||||
Assert.assertThrows(AssertionError.class, () -> {
|
||||
Assert.assertInstanceOf(Float.class, 1);
|
||||
}, e -> {
|
||||
Assert.assertContains("Expected: an instance of java.lang.Float",
|
||||
e.getMessage());
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.testutil;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class WhiteboxTest {
|
||||
|
||||
@Test
|
||||
public void testGetStaticInternalState() {
|
||||
Assert.assertEquals(1, Whitebox.getInternalState(Test1.class,
|
||||
"staticValue"));
|
||||
Test1 test1 = newTest();
|
||||
Assert.assertEquals(1, Whitebox.getInternalState(test1, "staticValue"));
|
||||
Assert.assertEquals(2, Whitebox.getInternalState(test1,
|
||||
"test2.staticValue"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetStaticInternalState() {
|
||||
try {
|
||||
Whitebox.setInternalState(Test1.class, "staticValue", 11);
|
||||
Assert.assertEquals(11, Test1.staticValue);
|
||||
|
||||
Test1 test1 = newTest();
|
||||
Whitebox.setInternalState(test1, "staticValue", 111);
|
||||
Assert.assertEquals(111, Test1.staticValue);
|
||||
|
||||
Whitebox.setInternalState(test1, "test2.staticValue", 22);
|
||||
Assert.assertEquals(22, Test2.staticValue);
|
||||
} finally {
|
||||
Whitebox.setInternalState(Test1.class, "staticValue", 1);
|
||||
Whitebox.setInternalState(Test2.class, "staticValue", 2);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInternalState() {
|
||||
Test1 test1 = newTest();
|
||||
Assert.assertEquals(1, Whitebox.getInternalState(test1, "ivalue"));
|
||||
Assert.assertEquals(2f, Whitebox.getInternalState(test1,
|
||||
"test2.finalValue"));
|
||||
Assert.assertEquals("3", Whitebox.getInternalState(test1,
|
||||
"test2.test3.str"));
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
Whitebox.getInternalState(test1, "ivalue2");
|
||||
});
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
Whitebox.getInternalState(test1, "test2.valueNotExist");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetInternalState() {
|
||||
Test1 test1 = newTest();
|
||||
|
||||
Whitebox.setInternalState(test1, "ivalue", 11);
|
||||
Assert.assertEquals(11, Whitebox.getInternalState(test1, "ivalue"));
|
||||
Assert.assertEquals(11, test1.ivalue);
|
||||
|
||||
Whitebox.setInternalState(test1, "test2.finalValue", 22f);
|
||||
Assert.assertEquals(22f, Whitebox.getInternalState(test1,
|
||||
"test2.finalValue"));
|
||||
|
||||
Whitebox.setInternalState(test1, "test2.test3.str", "33");
|
||||
Assert.assertEquals("33", Whitebox.getInternalState(test1,
|
||||
"test2.test3.str"));
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
Whitebox.setInternalState(test1, "ivalue2", 11);
|
||||
});
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
Whitebox.setInternalState(test1, "test2.valueNotExist", 22f);
|
||||
});
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
Whitebox.setInternalState(test1, "test2.finalValue", 22d);
|
||||
});
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
test1.test2 = null;
|
||||
Whitebox.setInternalState(test1, "test2.finalValue", 22f);
|
||||
}, e -> {
|
||||
Assert.assertContains("Can't set value on null field",
|
||||
e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetInternalFinalState() {
|
||||
Test1 test1 = newTest();
|
||||
Assert.assertEquals(1, test1.finalValue);
|
||||
|
||||
Whitebox.setInternalState(test1, "finalValue", 2);
|
||||
Assert.assertEquals(2, Whitebox.getInternalState(test1, "finalValue"));
|
||||
|
||||
Whitebox.setInternalState(test1, "finalValue", 3);
|
||||
Assert.assertEquals(3, Whitebox.getInternalState(test1, "finalValue"));
|
||||
|
||||
Whitebox.setInternalState(test1, "test2.finalValue", 22f);
|
||||
Assert.assertEquals(22f, Whitebox.getInternalState(test1,
|
||||
"test2.finalValue"));
|
||||
|
||||
// FIXME: seems don't take effect!!!
|
||||
Assert.assertEquals(1, test1.finalValue);
|
||||
Assert.assertEquals(2f, test1.test2.finalValue, 0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeStatic() {
|
||||
Assert.assertEquals(1, Whitebox.invokeStatic(Test1.class, "svalue"));
|
||||
Assert.assertEquals(2, Whitebox.invokeStatic(Test1.class, "svalue", 2));
|
||||
Assert.assertEquals(2, Whitebox.invokeStatic(Test1.class, "svalue",
|
||||
new Integer(2)));
|
||||
Assert.assertEquals(2d, Whitebox.invokeStatic(Test1.class,
|
||||
new Class[]{Object.class},
|
||||
"svalue", 2d));
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
Whitebox.invokeStatic(Test1.class, "svalue2");
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
Whitebox.invokeStatic(Test1.class, "throwfunc1");
|
||||
});
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
Whitebox.invokeStatic(Test1.class, "throwfunc2");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvoke() {
|
||||
Test1 test1 = newTest();
|
||||
Assert.assertEquals(1, Whitebox.invoke(test1.getClass(),
|
||||
"value", test1));
|
||||
Assert.assertEquals(3, Whitebox.invoke(test1.getClass(),
|
||||
"addValue", test1, 2));
|
||||
Assert.assertEquals(2f, Whitebox.invoke(test1, "test2", "value"));
|
||||
Assert.assertEquals(2, Whitebox.invoke(test1, "test2",
|
||||
new Class[]{Object.class},
|
||||
"value", 2));
|
||||
Assert.assertEquals(3, Whitebox.invoke(test1, "test4", "addValue", 2));
|
||||
Assert.assertEquals(4, Whitebox.invoke(test1, "test4", "value"));
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
Whitebox.invoke(test1.getClass(), "value2", test1);
|
||||
});
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
Whitebox.invoke(test1, "test22", "value");
|
||||
});
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
Whitebox.invoke(test1, "test2", "value", 2);
|
||||
});
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
Whitebox.invoke(test1.getClass(), "addValue", test1, 2.0);
|
||||
});
|
||||
}
|
||||
|
||||
private static Test1 newTest() {
|
||||
Test1 test1 = new Test1();
|
||||
test1.test2 = new Test2();
|
||||
test1.test2.test3 = new Test3();
|
||||
test1.test4 = new TestSubClass();
|
||||
return test1;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class Test1 {
|
||||
|
||||
private static int staticValue = 1;
|
||||
|
||||
private int ivalue = 1;
|
||||
private final int finalValue = 1;
|
||||
|
||||
private Test2 test2;
|
||||
private TestSubClass test4;
|
||||
|
||||
private int value() {
|
||||
return this.ivalue;
|
||||
}
|
||||
|
||||
private int addValue(int i) {
|
||||
return this.ivalue + i;
|
||||
}
|
||||
|
||||
private static int svalue() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int svalue(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
private static <T> T svalue(T o) {
|
||||
return o;
|
||||
}
|
||||
|
||||
private static int throwfunc1() {
|
||||
throw new IllegalArgumentException("fake runtime exception");
|
||||
}
|
||||
|
||||
private static int throwfunc2() throws Exception {
|
||||
throw new Exception("fake exception");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class Test2 {
|
||||
|
||||
private static int staticValue = 2;
|
||||
private final float finalValue = 2f;
|
||||
private Test3 test3;
|
||||
|
||||
private float value() {
|
||||
return this.finalValue;
|
||||
}
|
||||
|
||||
private <T> T value(T o) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class Test3 {
|
||||
|
||||
private String str = "3";
|
||||
|
||||
private String value() {
|
||||
return this.str;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestSubClass extends Test1 {
|
||||
|
||||
private int value() {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.unit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.hugegraph.util.ExceptionUtil;
|
||||
import org.apache.hugegraph.util.TimeUtil;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
public class BaseUnitTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void init() {
|
||||
// pass
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void clear() throws Exception {
|
||||
// pass
|
||||
}
|
||||
|
||||
protected static void runWithThreads(int threads, Runnable task) {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(threads);
|
||||
List<Future<?>> futures = new ArrayList<>();
|
||||
for (int i = 0; i < threads; i++) {
|
||||
futures.add(executor.submit(task));
|
||||
}
|
||||
for (Future<?> future : futures) {
|
||||
ExceptionUtil.futureGet(future);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void waitTillNext(long seconds) {
|
||||
TimeUtil.tillNextMillis(TimeUtil.timeGen() + seconds * 1000);
|
||||
}
|
||||
|
||||
public static void downloadFileByUrl(String url, String destPath) {
|
||||
int connectTimeout = 5000;
|
||||
int readTimeout = 5000;
|
||||
try {
|
||||
FileUtils.copyURLToFile(new URL(url), new File(destPath), connectTimeout, readTimeout);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.unit;
|
||||
|
||||
import org.apache.hugegraph.testutil.AssertTest;
|
||||
import org.apache.hugegraph.testutil.WhiteboxTest;
|
||||
import org.apache.hugegraph.unit.config.HugeConfigTest;
|
||||
import org.apache.hugegraph.unit.config.OptionSpaceTest;
|
||||
import org.apache.hugegraph.unit.event.EventHubTest;
|
||||
import org.apache.hugegraph.unit.version.VersionTest;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
|
||||
import org.apache.hugegraph.unit.concurrent.AtomicLockTest;
|
||||
import org.apache.hugegraph.unit.concurrent.BarrierEventTest;
|
||||
import org.apache.hugegraph.unit.concurrent.KeyLockTest;
|
||||
import org.apache.hugegraph.unit.concurrent.LockGroupTest;
|
||||
import org.apache.hugegraph.unit.concurrent.LockManagerTest;
|
||||
import org.apache.hugegraph.unit.concurrent.PausableScheduledThreadPoolTest;
|
||||
import org.apache.hugegraph.unit.concurrent.RowLockTest;
|
||||
import org.apache.hugegraph.unit.date.SafeDateFormatTest;
|
||||
import org.apache.hugegraph.unit.iterator.BatchMapperIteratorTest;
|
||||
import org.apache.hugegraph.unit.iterator.ExtendableIteratorTest;
|
||||
import org.apache.hugegraph.unit.iterator.FilterIteratorTest;
|
||||
import org.apache.hugegraph.unit.iterator.FlatMapperFilterIteratorTest;
|
||||
import org.apache.hugegraph.unit.iterator.FlatMapperIteratorTest;
|
||||
import org.apache.hugegraph.unit.iterator.LimitIteratorTest;
|
||||
import org.apache.hugegraph.unit.iterator.ListIteratorTest;
|
||||
import org.apache.hugegraph.unit.iterator.MapperIteratorTest;
|
||||
import org.apache.hugegraph.unit.license.LicenseExtraParamTest;
|
||||
import org.apache.hugegraph.unit.license.LicenseCreateParamTest;
|
||||
import org.apache.hugegraph.unit.license.LicenseInstallParamTest;
|
||||
import org.apache.hugegraph.unit.license.LicenseParamsTest;
|
||||
import org.apache.hugegraph.unit.license.MachineInfoTest;
|
||||
import org.apache.hugegraph.unit.perf.PerfUtilTest;
|
||||
import org.apache.hugegraph.unit.perf.StopwatchTest;
|
||||
import org.apache.hugegraph.unit.rest.RestClientTest;
|
||||
import org.apache.hugegraph.unit.rest.RestResultTest;
|
||||
import org.apache.hugegraph.unit.util.BytesTest;
|
||||
import org.apache.hugegraph.unit.util.CollectionUtilTest;
|
||||
import org.apache.hugegraph.unit.util.DateUtilTest;
|
||||
import org.apache.hugegraph.unit.util.EcheckTest;
|
||||
import org.apache.hugegraph.unit.util.HashUtilTest;
|
||||
import org.apache.hugegraph.unit.util.InsertionOrderUtilTest;
|
||||
import org.apache.hugegraph.unit.util.LogTest;
|
||||
import org.apache.hugegraph.unit.util.LongEncodingTest;
|
||||
import org.apache.hugegraph.unit.util.NumericUtilTest;
|
||||
import org.apache.hugegraph.unit.util.OrderLimitMapTest;
|
||||
import org.apache.hugegraph.unit.util.ReflectionUtilTest;
|
||||
import org.apache.hugegraph.unit.util.StringUtilTest;
|
||||
import org.apache.hugegraph.unit.util.TimeUtilTest;
|
||||
import org.apache.hugegraph.unit.util.UnitUtilTest;
|
||||
import org.apache.hugegraph.unit.util.VersionUtilTest;
|
||||
|
||||
@RunWith(Suite.class)
|
||||
@Suite.SuiteClasses({
|
||||
LockManagerTest.class,
|
||||
LockGroupTest.class,
|
||||
AtomicLockTest.class,
|
||||
KeyLockTest.class,
|
||||
RowLockTest.class,
|
||||
PausableScheduledThreadPoolTest.class,
|
||||
|
||||
HugeConfigTest.class,
|
||||
OptionSpaceTest.class,
|
||||
SafeDateFormatTest.class,
|
||||
BarrierEventTest.class,
|
||||
EventHubTest.class,
|
||||
PerfUtilTest.class,
|
||||
StopwatchTest.class,
|
||||
RestClientTest.class,
|
||||
RestResultTest.class,
|
||||
VersionTest.class,
|
||||
|
||||
ExtendableIteratorTest.class,
|
||||
FilterIteratorTest.class,
|
||||
LimitIteratorTest.class,
|
||||
MapperIteratorTest.class,
|
||||
FlatMapperIteratorTest.class,
|
||||
FlatMapperFilterIteratorTest.class,
|
||||
ListIteratorTest.class,
|
||||
BatchMapperIteratorTest.class,
|
||||
|
||||
BytesTest.class,
|
||||
CollectionUtilTest.class,
|
||||
EcheckTest.class,
|
||||
HashUtilTest.class,
|
||||
InsertionOrderUtilTest.class,
|
||||
LogTest.class,
|
||||
NumericUtilTest.class,
|
||||
ReflectionUtilTest.class,
|
||||
StringUtilTest.class,
|
||||
TimeUtilTest.class,
|
||||
VersionUtilTest.class,
|
||||
LongEncodingTest.class,
|
||||
OrderLimitMapTest.class,
|
||||
DateUtilTest.class,
|
||||
UnitUtilTest.class,
|
||||
|
||||
LicenseExtraParamTest.class,
|
||||
LicenseCreateParamTest.class,
|
||||
LicenseInstallParamTest.class,
|
||||
LicenseParamsTest.class,
|
||||
MachineInfoTest.class,
|
||||
|
||||
AssertTest.class,
|
||||
WhiteboxTest.class
|
||||
})
|
||||
public class UnitTestSuite {
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.unit.concurrent;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.hugegraph.concurrent.AtomicLock;
|
||||
import org.apache.hugegraph.testutil.Assert;
|
||||
import org.apache.hugegraph.unit.BaseUnitTest;
|
||||
|
||||
public class AtomicLockTest extends BaseUnitTest {
|
||||
|
||||
@Test
|
||||
public void testLockUnlock() {
|
||||
AtomicLock lock = new AtomicLock("lock");
|
||||
Assert.assertEquals("lock", lock.name());
|
||||
|
||||
Assert.assertTrue(lock.lock(0));
|
||||
try {
|
||||
Assert.assertFalse(lock.lock(1));
|
||||
// lock in other threads
|
||||
runWithThreads(2, () -> {
|
||||
Assert.assertFalse(lock.tryLock());
|
||||
});
|
||||
lock.unlock();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
// unlock multi times is OK
|
||||
lock.unlock();
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
lock.lock(-1);
|
||||
}, e -> {
|
||||
Assert.assertContains("Locking retry times should be in [0, 10], " +
|
||||
"but got -1", e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
lock.lock(11);
|
||||
}, e -> {
|
||||
Assert.assertContains("Locking retry times should be in [0, 10], " +
|
||||
"but got 11", e.getMessage());
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.unit.concurrent;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.hugegraph.concurrent.BarrierEvent;
|
||||
import org.apache.hugegraph.testutil.Assert;
|
||||
|
||||
public class BarrierEventTest {
|
||||
|
||||
private static final int WAIT_THREADS_COUNT = 10;
|
||||
|
||||
@Test(timeout = 5000)
|
||||
public void testAWait() throws InterruptedException {
|
||||
BarrierEvent barrierEvent = new BarrierEvent();
|
||||
AtomicInteger result = new AtomicInteger(0);
|
||||
CountDownLatch latch = new CountDownLatch(2);
|
||||
Thread awaitThread = new Thread(() -> {
|
||||
try {
|
||||
barrierEvent.await();
|
||||
result.incrementAndGet();
|
||||
} catch (InterruptedException e) {
|
||||
// Do nothing.
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
awaitThread.start();
|
||||
Thread signalThread = new Thread(() -> {
|
||||
barrierEvent.signalAll();
|
||||
latch.countDown();
|
||||
});
|
||||
signalThread.start();
|
||||
latch.await();
|
||||
Assert.assertEquals(1, result.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAWaitWithTimeout() throws InterruptedException {
|
||||
BarrierEvent barrierEvent = new BarrierEvent();
|
||||
boolean signaled = barrierEvent.await(1L);
|
||||
Assert.assertFalse(signaled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReset() throws InterruptedException {
|
||||
BarrierEvent barrierEvent = new BarrierEvent();
|
||||
boolean signaled = barrierEvent.await(1L);
|
||||
Assert.assertFalse(signaled);
|
||||
barrierEvent.signal();
|
||||
signaled = barrierEvent.await(1L);
|
||||
Assert.assertTrue(signaled);
|
||||
barrierEvent.reset();
|
||||
signaled = barrierEvent.await(1L);
|
||||
Assert.assertFalse(signaled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignal() throws InterruptedException {
|
||||
BarrierEvent barrierEvent = new BarrierEvent();
|
||||
boolean signaled = barrierEvent.await(1L);
|
||||
Assert.assertFalse(signaled);
|
||||
barrierEvent.signal();
|
||||
signaled = barrierEvent.await(1L);
|
||||
Assert.assertTrue(signaled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignalByMultiThreadWithSignalFirst()
|
||||
throws InterruptedException {
|
||||
BarrierEvent barrierEvent = new BarrierEvent();
|
||||
AtomicInteger eventCount = new AtomicInteger(0);
|
||||
AtomicInteger waitThreadInterruptedCount = new AtomicInteger(0);
|
||||
ExecutorService executorService =
|
||||
Executors.newFixedThreadPool(WAIT_THREADS_COUNT + 1);
|
||||
CountDownLatch waitLatch = new CountDownLatch(WAIT_THREADS_COUNT);
|
||||
CountDownLatch signalLatch = new CountDownLatch(1);
|
||||
for (int i = 0; i < WAIT_THREADS_COUNT; i++) {
|
||||
executorService.submit(() -> {
|
||||
try {
|
||||
signalLatch.await();
|
||||
barrierEvent.await();
|
||||
eventCount.incrementAndGet();
|
||||
} catch (InterruptedException e) {
|
||||
waitThreadInterruptedCount.incrementAndGet();
|
||||
} finally {
|
||||
waitLatch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executorService.submit(() -> {
|
||||
barrierEvent.signal();
|
||||
signalLatch.countDown();
|
||||
});
|
||||
|
||||
executorService.shutdown();
|
||||
executorService.awaitTermination(2L, TimeUnit.SECONDS);
|
||||
waitLatch.await();
|
||||
Assert.assertEquals(10, eventCount.get());
|
||||
Assert.assertEquals(0, waitThreadInterruptedCount.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignalByMultiThreadWithSignalLast()
|
||||
throws InterruptedException {
|
||||
BarrierEvent barrierEvent = new BarrierEvent();
|
||||
AtomicInteger eventCount = new AtomicInteger(0);
|
||||
AtomicInteger waitThreadInterruptedCount = new AtomicInteger(0);
|
||||
AtomicInteger signalThreadInterruptedCount = new AtomicInteger(0);
|
||||
ExecutorService executorService =
|
||||
Executors.newFixedThreadPool(WAIT_THREADS_COUNT + 1);
|
||||
CountDownLatch waitLatch = new CountDownLatch(WAIT_THREADS_COUNT);
|
||||
CountDownLatch signalLatch = new CountDownLatch(1);
|
||||
for (int i = 0; i < WAIT_THREADS_COUNT; i++) {
|
||||
executorService.submit(() -> {
|
||||
try {
|
||||
waitLatch.countDown();
|
||||
barrierEvent.await();
|
||||
eventCount.incrementAndGet();
|
||||
} catch (InterruptedException e) {
|
||||
waitThreadInterruptedCount.incrementAndGet();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executorService.submit(() -> {
|
||||
try {
|
||||
waitLatch.await();
|
||||
} catch (InterruptedException e) {
|
||||
signalThreadInterruptedCount.incrementAndGet();
|
||||
}
|
||||
barrierEvent.signal();
|
||||
signalLatch.countDown();
|
||||
});
|
||||
signalLatch.await();
|
||||
executorService.shutdownNow();
|
||||
executorService.awaitTermination(1L, TimeUnit.SECONDS);
|
||||
Assert.assertEquals(1, eventCount.get());
|
||||
Assert.assertEquals(WAIT_THREADS_COUNT - 1,
|
||||
waitThreadInterruptedCount.get());
|
||||
Assert.assertEquals(0, signalThreadInterruptedCount.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignalAll() throws InterruptedException {
|
||||
BarrierEvent barrierEvent = new BarrierEvent();
|
||||
boolean signaled = barrierEvent.await(1L);
|
||||
Assert.assertFalse(signaled);
|
||||
barrierEvent.signalAll();
|
||||
signaled = barrierEvent.await(1L);
|
||||
Assert.assertTrue(signaled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignalAllByMultiThreadWithSignalFirst()
|
||||
throws InterruptedException {
|
||||
BarrierEvent barrierEvent = new BarrierEvent();
|
||||
AtomicInteger eventCount = new AtomicInteger(0);
|
||||
AtomicInteger waitThreadInterruptedCount = new AtomicInteger(0);
|
||||
ExecutorService executorService =
|
||||
Executors.newFixedThreadPool(WAIT_THREADS_COUNT + 1);
|
||||
CountDownLatch waitLatch = new CountDownLatch(WAIT_THREADS_COUNT);
|
||||
CountDownLatch signalLatch = new CountDownLatch(1);
|
||||
for (int i = 0; i < WAIT_THREADS_COUNT; i++) {
|
||||
executorService.submit(() -> {
|
||||
try {
|
||||
signalLatch.await();
|
||||
waitLatch.countDown();
|
||||
barrierEvent.await();
|
||||
eventCount.incrementAndGet();
|
||||
} catch (InterruptedException e) {
|
||||
waitThreadInterruptedCount.incrementAndGet();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executorService.submit(() -> {
|
||||
barrierEvent.signalAll();
|
||||
signalLatch.countDown();
|
||||
});
|
||||
|
||||
executorService.shutdown();
|
||||
executorService.awaitTermination(1L, TimeUnit.SECONDS);
|
||||
Assert.assertEquals(10, eventCount.get());
|
||||
Assert.assertEquals(0, waitThreadInterruptedCount.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignalAllByMultiThreadWithSignalLast()
|
||||
throws InterruptedException {
|
||||
BarrierEvent barrierEvent = new BarrierEvent();
|
||||
AtomicInteger eventCount = new AtomicInteger(0);
|
||||
AtomicInteger waitThreadInterruptedCount = new AtomicInteger(0);
|
||||
AtomicInteger signalThreadInterruptedCount = new AtomicInteger(0);
|
||||
ExecutorService executorService =
|
||||
Executors.newFixedThreadPool(WAIT_THREADS_COUNT + 1);
|
||||
CountDownLatch waitLatch = new CountDownLatch(WAIT_THREADS_COUNT);
|
||||
CountDownLatch signalLatch = new CountDownLatch(1);
|
||||
for (int i = 0; i < WAIT_THREADS_COUNT; i++) {
|
||||
executorService.submit(() -> {
|
||||
try {
|
||||
waitLatch.countDown();
|
||||
barrierEvent.await();
|
||||
eventCount.incrementAndGet();
|
||||
} catch (InterruptedException e) {
|
||||
waitThreadInterruptedCount.incrementAndGet();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executorService.submit(() -> {
|
||||
try {
|
||||
waitLatch.await();
|
||||
} catch (InterruptedException e) {
|
||||
signalThreadInterruptedCount.incrementAndGet();
|
||||
}
|
||||
barrierEvent.signalAll();
|
||||
signalLatch.countDown();
|
||||
});
|
||||
signalLatch.await();
|
||||
executorService.shutdown();
|
||||
executorService.awaitTermination(1L, TimeUnit.SECONDS);
|
||||
Assert.assertEquals(WAIT_THREADS_COUNT, eventCount.get());
|
||||
Assert.assertEquals(0, waitThreadInterruptedCount.get());
|
||||
Assert.assertEquals(0, signalThreadInterruptedCount.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignalAllByMultiThreadWithSignalAwaitConcurrent()
|
||||
throws InterruptedException {
|
||||
BarrierEvent barrierEvent = new BarrierEvent();
|
||||
AtomicInteger eventCount = new AtomicInteger(0);
|
||||
AtomicInteger waitThreadInterruptedCount = new AtomicInteger(0);
|
||||
AtomicInteger signalThreadInterruptedCount = new AtomicInteger(0);
|
||||
ExecutorService executorService =
|
||||
Executors.newFixedThreadPool(WAIT_THREADS_COUNT + 1);
|
||||
CountDownLatch syncLatch = new CountDownLatch(1);
|
||||
for (int i = 0; i < WAIT_THREADS_COUNT; i++) {
|
||||
executorService.submit(() -> {
|
||||
try {
|
||||
syncLatch.await();
|
||||
barrierEvent.await();
|
||||
eventCount.incrementAndGet();
|
||||
} catch (InterruptedException e) {
|
||||
waitThreadInterruptedCount.incrementAndGet();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executorService.submit(() -> {
|
||||
try {
|
||||
syncLatch.await();
|
||||
} catch (InterruptedException e) {
|
||||
signalThreadInterruptedCount.incrementAndGet();
|
||||
}
|
||||
barrierEvent.signalAll();
|
||||
});
|
||||
syncLatch.countDown();
|
||||
executorService.shutdown();
|
||||
executorService.awaitTermination(1L, TimeUnit.SECONDS);
|
||||
Assert.assertEquals(WAIT_THREADS_COUNT, eventCount.get());
|
||||
Assert.assertEquals(0, waitThreadInterruptedCount.get());
|
||||
Assert.assertEquals(0, signalThreadInterruptedCount.get());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.unit.concurrent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.hugegraph.concurrent.KeyLock;
|
||||
import org.apache.hugegraph.testutil.Assert;
|
||||
import org.apache.hugegraph.unit.BaseUnitTest;
|
||||
|
||||
public class KeyLockTest extends BaseUnitTest {
|
||||
|
||||
@Test
|
||||
public void testLockUnlock() {
|
||||
KeyLock locks = new KeyLock();
|
||||
|
||||
locks.lock("1");
|
||||
try {
|
||||
// lock again is OK
|
||||
locks.lock("1");
|
||||
// lock in other threads
|
||||
runWithThreads(1, () -> {
|
||||
locks.lock("2");
|
||||
});
|
||||
locks.unlock("1");
|
||||
} finally {
|
||||
locks.unlock("1");
|
||||
}
|
||||
|
||||
Assert.assertThrows(IllegalMonitorStateException.class, () -> {
|
||||
locks.unlock("2");
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalMonitorStateException.class, () -> {
|
||||
locks.unlock("3");
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
locks.lock(null);
|
||||
}, e -> {
|
||||
Assert.assertContains("Lock key can't be null", e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
locks.unlock(null);
|
||||
}, e -> {
|
||||
Assert.assertContains("Unlock key can't be null", e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLockUnlockAll() {
|
||||
KeyLock locks = new KeyLock();
|
||||
|
||||
List<Lock> ls = locks.lockAll("1", 2);
|
||||
locks.unlockAll(ls);
|
||||
|
||||
runWithThreads(1, () -> {
|
||||
List<Lock> ls2 = locks.lockAll("1", 3);
|
||||
locks.unlockAll(ls2);
|
||||
});
|
||||
|
||||
List<Lock> ls3 = locks.lockAll("1", 2, 3);
|
||||
locks.unlockAll(ls3);
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
locks.lockAll("1", null);
|
||||
}, e -> {
|
||||
Assert.assertContains("Lock key can't be null", e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
locks.lockAll(null, "1");
|
||||
}, e -> {
|
||||
Assert.assertContains("Lock key can't be null", e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
locks.lockAll(Arrays.asList("1", null, 2).toArray());
|
||||
}, e -> {
|
||||
Assert.assertContains("Lock key can't be null", e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
locks.lockAll(Collections.emptyList().toArray());
|
||||
}, e -> {
|
||||
Assert.assertContains("Lock keys can't be null or empty",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
locks.lockAll((Object[]) null);
|
||||
}, e -> {
|
||||
Assert.assertContains("Lock keys can't be null or empty",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
locks.unlockAll(null);
|
||||
}, e -> {
|
||||
Assert.assertContains("Unlock locks can't be null", e.getMessage());
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.unit.concurrent;
|
||||
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.hugegraph.concurrent.AtomicLock;
|
||||
import org.apache.hugegraph.concurrent.KeyLock;
|
||||
import org.apache.hugegraph.concurrent.LockGroup;
|
||||
import org.apache.hugegraph.concurrent.RowLock;
|
||||
import org.apache.hugegraph.testutil.Assert;
|
||||
import org.apache.hugegraph.unit.BaseUnitTest;
|
||||
|
||||
public class LockGroupTest extends BaseUnitTest {
|
||||
|
||||
private static final String GROUP = "LockGroupTest-test-group";
|
||||
|
||||
private final LockGroup group = new LockGroup(GROUP);
|
||||
|
||||
@Test
|
||||
public void testLock() {
|
||||
Lock lock = this.group.lock("lock");
|
||||
Assert.assertTrue(lock instanceof ReentrantLock);
|
||||
Lock lock1 = this.group.lock("lock");
|
||||
Assert.assertSame(lock, lock1);
|
||||
|
||||
lock1.lock();
|
||||
try {
|
||||
// lock again is OK
|
||||
lock1.lock();
|
||||
// lock in other threads
|
||||
runWithThreads(2, () -> {
|
||||
Assert.assertFalse(lock1.tryLock());
|
||||
});
|
||||
lock1.unlock();
|
||||
} finally {
|
||||
lock1.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAtomicLock() {
|
||||
AtomicLock lock = this.group.atomicLock("lock");
|
||||
Assert.assertNotNull(lock);
|
||||
AtomicLock lock1 = this.group.atomicLock("lock");
|
||||
Assert.assertSame(lock, lock1);
|
||||
Assert.assertEquals("lock", lock1.name());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWriteLock() {
|
||||
ReadWriteLock lock = this.group.readWriteLock("lock");
|
||||
Assert.assertTrue(lock instanceof ReentrantReadWriteLock);
|
||||
ReadWriteLock lock1 = this.group.readWriteLock("lock");
|
||||
Assert.assertSame(lock, lock1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyLock() {
|
||||
KeyLock lock = this.group.keyLock("lock");
|
||||
Assert.assertNotNull(lock);
|
||||
KeyLock lock1 = this.group.keyLock("lock");
|
||||
Assert.assertSame(lock, lock1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyLockWithSize() {
|
||||
KeyLock lock = this.group.keyLock("lock", 10);
|
||||
Assert.assertNotNull(lock);
|
||||
KeyLock lock1 = this.group.keyLock("lock");
|
||||
Assert.assertSame(lock, lock1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRowLock() {
|
||||
RowLock<?> lock = this.group.rowLock("lock");
|
||||
Assert.assertNotNull(lock);
|
||||
RowLock<?> lock1 = this.group.rowLock("lock");
|
||||
Assert.assertSame(lock, lock1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testName() {
|
||||
Assert.assertEquals(GROUP, this.group.name());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.unit.concurrent;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.hugegraph.concurrent.LockGroup;
|
||||
import org.apache.hugegraph.concurrent.LockManager;
|
||||
import org.apache.hugegraph.testutil.Assert;
|
||||
import org.apache.hugegraph.unit.BaseUnitTest;
|
||||
|
||||
public class LockManagerTest extends BaseUnitTest {
|
||||
|
||||
private static final String GROUP = "LockManagerTest-test-group";
|
||||
private static final String GROUP2 = GROUP + 2;
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
LockManager manager = LockManager.instance();
|
||||
|
||||
if (manager.exists(GROUP)) {
|
||||
manager.destroy(GROUP);
|
||||
}
|
||||
|
||||
if (manager.exists(GROUP2)) {
|
||||
manager.destroy(GROUP2);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
LockManager manager = LockManager.instance();
|
||||
|
||||
LockGroup lockGroup = manager.create(GROUP);
|
||||
Assert.assertNotNull(lockGroup);
|
||||
Assert.assertEquals(GROUP, lockGroup.name());
|
||||
Assert.assertTrue(manager.exists(GROUP));
|
||||
|
||||
Assert.assertFalse(manager.exists(GROUP2));
|
||||
LockGroup lockGroup2 = manager.create(GROUP2);
|
||||
Assert.assertNotNull(lockGroup2);
|
||||
Assert.assertEquals(GROUP2, lockGroup2.name());
|
||||
Assert.assertTrue(manager.exists(GROUP2));
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
manager.create(GROUP);
|
||||
}, e -> {
|
||||
Assert.assertContains("LockGroup 'LockManagerTest-test-group' " +
|
||||
"already exists", e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGet() {
|
||||
LockManager manager = LockManager.instance();
|
||||
|
||||
LockGroup lockGroup = manager.create(GROUP);
|
||||
LockGroup lockGroup2 = manager.create(GROUP2);
|
||||
|
||||
Assert.assertSame(lockGroup, manager.get(GROUP));
|
||||
Assert.assertSame(lockGroup2, manager.get(GROUP2));
|
||||
Assert.assertSame(lockGroup, manager.get(GROUP));
|
||||
Assert.assertSame(lockGroup2, manager.get(GROUP2));
|
||||
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
manager.get("fake-lock-group");
|
||||
}, e -> {
|
||||
Assert.assertContains("LockGroup 'fake-lock-group' " +
|
||||
"does not exists", e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDestroy() {
|
||||
LockManager manager = LockManager.instance();
|
||||
|
||||
LockGroup lockGroup = manager.create(GROUP);
|
||||
LockGroup lockGroup2 = manager.create(GROUP2);
|
||||
|
||||
Assert.assertTrue(manager.exists(GROUP));
|
||||
Assert.assertTrue(manager.exists(GROUP2));
|
||||
Assert.assertSame(lockGroup, manager.get(GROUP));
|
||||
Assert.assertSame(lockGroup2, manager.get(GROUP2));
|
||||
|
||||
manager.destroy(GROUP);
|
||||
Assert.assertFalse(manager.exists(GROUP));
|
||||
Assert.assertTrue(manager.exists(GROUP2));
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
manager.get(GROUP);
|
||||
}, e -> {
|
||||
Assert.assertContains("does not exists", e.getMessage());
|
||||
});
|
||||
Assert.assertSame(lockGroup2, manager.get(GROUP2));
|
||||
|
||||
manager.destroy(GROUP2);
|
||||
Assert.assertFalse(manager.exists(GROUP));
|
||||
Assert.assertFalse(manager.exists(GROUP2));
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
manager.get(GROUP);
|
||||
}, e -> {
|
||||
Assert.assertContains("does not exists", e.getMessage());
|
||||
});
|
||||
Assert.assertThrows(RuntimeException.class, () -> {
|
||||
manager.get(GROUP2);
|
||||
}, e -> {
|
||||
Assert.assertContains("does not exists", e.getMessage());
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.unit.concurrent;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.hugegraph.util.ExecutorUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.hugegraph.concurrent.PausableScheduledThreadPool;
|
||||
|
||||
public class PausableScheduledThreadPoolTest {
|
||||
|
||||
@Test
|
||||
public void testScheduleWithFixedDelay() throws InterruptedException {
|
||||
PausableScheduledThreadPool executor =
|
||||
ExecutorUtil.newPausableScheduledThreadPool("test");
|
||||
long period = 500L;
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
executor.scheduleWithFixedDelay(() -> {
|
||||
System.out.println("counter: " + counter.incrementAndGet());
|
||||
}, period, period, TimeUnit.MILLISECONDS);
|
||||
|
||||
Thread.sleep((long) (2.1 * period));
|
||||
Assert.assertEquals(2, counter.get());
|
||||
|
||||
// pause
|
||||
executor.pauseSchedule();
|
||||
Thread.sleep(period);
|
||||
Assert.assertEquals(2, counter.get());
|
||||
|
||||
// resume
|
||||
executor.resumeSchedule();
|
||||
Thread.sleep((long) (0.5 * period));
|
||||
Assert.assertEquals(3, counter.get());
|
||||
|
||||
Thread.sleep((long) (0.6 * period));
|
||||
Assert.assertEquals(4, counter.get());
|
||||
|
||||
// pause again
|
||||
executor.pauseSchedule();
|
||||
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(3L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleWithFixedRate() throws InterruptedException {
|
||||
PausableScheduledThreadPool executor =
|
||||
ExecutorUtil.newPausableScheduledThreadPool(2, "test");
|
||||
long period = 500L;
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
executor.scheduleAtFixedRate(() -> {
|
||||
System.out.println("counter: " + counter.incrementAndGet());
|
||||
}, period, period, TimeUnit.MILLISECONDS);
|
||||
|
||||
Thread.sleep((long) (2.1 * period));
|
||||
Assert.assertEquals(2, counter.get());
|
||||
|
||||
// pause
|
||||
executor.pauseSchedule();
|
||||
Thread.sleep(period);
|
||||
Assert.assertEquals(2, counter.get());
|
||||
|
||||
// resume
|
||||
executor.resumeSchedule();
|
||||
Thread.sleep((long) (1.1 * period));
|
||||
Assert.assertEquals(4, counter.get());
|
||||
|
||||
// pause again
|
||||
executor.pauseSchedule();
|
||||
|
||||
executor.shutdownNow();
|
||||
executor.awaitTermination(3L, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.unit.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.hugegraph.concurrent.RowLock;
|
||||
import org.apache.hugegraph.testutil.Assert;
|
||||
import org.apache.hugegraph.unit.BaseUnitTest;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
public class RowLockTest extends BaseUnitTest {
|
||||
|
||||
private static final int THREADS_NUM = 8;
|
||||
|
||||
@Test
|
||||
public void testRowLock() {
|
||||
RowLock<Integer> lock = new RowLock<>();
|
||||
// Regular lock and unlock
|
||||
lock.lock(1);
|
||||
lock.unlock(1);
|
||||
|
||||
// Lock one lock multiple times
|
||||
lock.lock(1);
|
||||
lock.lock(1);
|
||||
lock.unlock(1);
|
||||
lock.unlock(1);
|
||||
|
||||
// Unlock one lock multiple times
|
||||
lock.lock(1);
|
||||
lock.unlock(1);
|
||||
lock.unlock(1);
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
lock.lock(null);
|
||||
}, e -> {
|
||||
Assert.assertContains("Lock key can't be null", e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
lock.unlock(null);
|
||||
}, e -> {
|
||||
Assert.assertContains("Unlock key can't be null", e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRowLockMultiRows() {
|
||||
RowLock<Integer> lock = new RowLock<>();
|
||||
lock.lockAll(ImmutableSet.of(1, 2, 3));
|
||||
lock.unlockAll(ImmutableSet.of(1, 2, 3));
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
lock.lockAll(null);
|
||||
}, e -> {
|
||||
Assert.assertContains("Lock keys can't be null", e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
lock.unlockAll(null);
|
||||
}, e -> {
|
||||
Assert.assertContains("Unlock keys can't be null", e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
lock.lockAll(ImmutableSet.of());
|
||||
}, e -> {
|
||||
Assert.assertContains("Lock keys can't be null or empty",
|
||||
e.getMessage());
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
lock.unlockAll(ImmutableSet.of());
|
||||
}, e -> {
|
||||
Assert.assertContains("Unlock keys can't be null or empty",
|
||||
e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRowLockWithMultiThreads() {
|
||||
RowLock<Integer> lock = new RowLock<>();
|
||||
Set<String> names = new HashSet<>(THREADS_NUM);
|
||||
List<Integer> keys = new ArrayList<>(5);
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
keys.add(random.nextInt(THREADS_NUM));
|
||||
}
|
||||
|
||||
Assert.assertEquals(0, names.size());
|
||||
|
||||
runWithThreads(THREADS_NUM, () -> {
|
||||
lock.lockAll(new HashSet<>(keys));
|
||||
names.add(Thread.currentThread().getName());
|
||||
lock.unlockAll(new HashSet<>(keys));
|
||||
});
|
||||
|
||||
Assert.assertEquals(THREADS_NUM, names.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRowLockWithMultiThreadsLockOneKey() {
|
||||
RowLock<Integer> lock = new RowLock<>();
|
||||
Set<String> names = new HashSet<>(THREADS_NUM);
|
||||
|
||||
Assert.assertEquals(0, names.size());
|
||||
|
||||
Integer key = 1;
|
||||
runWithThreads(THREADS_NUM, () -> {
|
||||
lock.lock(key);
|
||||
names.add(Thread.currentThread().getName());
|
||||
lock.unlock(key);
|
||||
});
|
||||
|
||||
Assert.assertEquals(THREADS_NUM, names.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRowLockWithMultiThreadsWithRandomKey() {
|
||||
RowLock<Integer> lock = new RowLock<>();
|
||||
Set<String> names = new HashSet<>(THREADS_NUM);
|
||||
|
||||
Assert.assertEquals(0, names.size());
|
||||
|
||||
runWithThreads(THREADS_NUM, () -> {
|
||||
List<Integer> keys = new ArrayList<>(5);
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
keys.add(random.nextInt(THREADS_NUM));
|
||||
}
|
||||
lock.lockAll(new HashSet<>(keys));
|
||||
names.add(Thread.currentThread().getName());
|
||||
lock.unlockAll(new HashSet<>(keys));
|
||||
});
|
||||
|
||||
Assert.assertEquals(THREADS_NUM, names.size());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,673 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.unit.config;
|
||||
|
||||
import static org.apache.hugegraph.config.OptionChecker.allowValues;
|
||||
import static org.apache.hugegraph.config.OptionChecker.disallowEmpty;
|
||||
import static org.apache.hugegraph.config.OptionChecker.inValues;
|
||||
import static org.apache.hugegraph.config.OptionChecker.nonNegativeInt;
|
||||
import static org.apache.hugegraph.config.OptionChecker.positiveInt;
|
||||
import static org.apache.hugegraph.config.OptionChecker.rangeDouble;
|
||||
import static org.apache.hugegraph.config.OptionChecker.rangeInt;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.hugegraph.config.ConfigConvOption;
|
||||
import org.apache.hugegraph.config.ConfigException;
|
||||
import org.apache.hugegraph.config.ConfigListConvOption;
|
||||
import org.apache.hugegraph.config.ConfigListOption;
|
||||
import org.apache.hugegraph.config.ConfigOption;
|
||||
import org.apache.hugegraph.config.HugeConfig;
|
||||
import org.apache.hugegraph.config.OptionHolder;
|
||||
import org.apache.hugegraph.config.OptionSpace;
|
||||
import org.apache.hugegraph.testutil.Assert;
|
||||
import org.apache.hugegraph.unit.BaseUnitTest;
|
||||
import org.apache.commons.collections.IteratorUtils;
|
||||
import org.apache.commons.configuration2.AbstractConfiguration;
|
||||
import org.apache.commons.configuration2.Configuration;
|
||||
import org.apache.commons.configuration2.MapConfiguration;
|
||||
import org.apache.commons.configuration2.PropertiesConfiguration;
|
||||
import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler;
|
||||
import org.apache.commons.configuration2.ex.ConfigurationException;
|
||||
import org.apache.commons.configuration2.io.FileHandler;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public class HugeConfigTest extends BaseUnitTest {
|
||||
|
||||
private static final String PATH =
|
||||
"src/test/java/org/apache/hugegraph/unit/config/";
|
||||
private static final String CONF = PATH + "test.conf";
|
||||
|
||||
@BeforeClass
|
||||
public static void init() {
|
||||
OptionSpace.register("test", TestOptions.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionDataType() {
|
||||
Assert.assertEquals(String.class, TestOptions.text1.dataType());
|
||||
Assert.assertEquals(Integer.class, TestOptions.int1.dataType());
|
||||
Assert.assertEquals(Long.class, TestOptions.long1.dataType());
|
||||
Assert.assertEquals(Float.class, TestOptions.float1.dataType());
|
||||
Assert.assertEquals(Double.class, TestOptions.double1.dataType());
|
||||
Assert.assertEquals(Boolean.class, TestOptions.bool.dataType());
|
||||
|
||||
Assert.assertEquals(Class.class, TestOptions.clazz.dataType());
|
||||
|
||||
Assert.assertEquals(List.class, TestOptions.list.dataType());
|
||||
Assert.assertEquals(List.class, TestOptions.map.dataType());
|
||||
|
||||
Assert.assertEquals(String.class, TestOptions.weekday.dataType());
|
||||
Assert.assertEquals(List.class, TestOptions.weekdays.dataType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionDesc() {
|
||||
Assert.assertEquals("description of group1.text1",
|
||||
TestOptions.text1.desc());
|
||||
Assert.assertEquals("description of group1.text2 sub",
|
||||
TestSubOptions.text2.desc());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionRequired() {
|
||||
Assert.assertFalse(TestOptions.text1.required());
|
||||
Assert.assertTrue(TestSubOptions.text2.required());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionsToString() {
|
||||
Assert.assertEquals("[String]group1.text1=text1-value",
|
||||
TestOptions.text1.toString());
|
||||
Assert.assertEquals("[Integer]group1.int1=1",
|
||||
TestOptions.int1.toString());
|
||||
Assert.assertEquals("[Long]group1.long1=100",
|
||||
TestOptions.long1.toString());
|
||||
Assert.assertEquals("[Float]group1.float1=100.0",
|
||||
TestOptions.float1.toString());
|
||||
Assert.assertEquals("[Double]group1.double1=100.0",
|
||||
TestOptions.double1.toString());
|
||||
Assert.assertEquals("[Boolean]group1.bool=true",
|
||||
TestOptions.bool.toString());
|
||||
Assert.assertEquals("[Class]group1.class=class java.lang.Object",
|
||||
TestOptions.clazz.toString());
|
||||
Assert.assertEquals("[List]group1.list=[list-value1, list-value2]",
|
||||
TestOptions.list.toString());
|
||||
Assert.assertEquals("[List]group1.map=[key1:value1, key2:value2]",
|
||||
TestOptions.map.toString());
|
||||
|
||||
Assert.assertEquals("[String]group1.text1=text1-value",
|
||||
TestSubOptions.text1.toString());
|
||||
Assert.assertEquals("[String]group1.text2=text2-value-override",
|
||||
TestSubOptions.text2.toString());
|
||||
Assert.assertEquals("[String]group1.textsub=textsub-value",
|
||||
TestSubOptions.textsub.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionWithError() {
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new ConfigOption<>(
|
||||
"group1.text",
|
||||
"description of group1.text",
|
||||
disallowEmpty(),
|
||||
""
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new ConfigOption<>(
|
||||
"group1.choice",
|
||||
"description of group1.choice",
|
||||
allowValues("CHOICE-1", "CHOICE-2", "CHOICE-3"),
|
||||
"CHOICE-4"
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new ConfigListOption<>(
|
||||
"group1.list",
|
||||
true,
|
||||
"description of group1.list",
|
||||
disallowEmpty(),
|
||||
String.class,
|
||||
ImmutableList.of()
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new ConfigOption<>(
|
||||
"group1.int",
|
||||
"description of group1.int",
|
||||
positiveInt(),
|
||||
0
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new ConfigOption<>(
|
||||
"group1.int",
|
||||
"description of group1.int",
|
||||
nonNegativeInt(),
|
||||
-1
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new ConfigOption<>(
|
||||
"group1.long",
|
||||
"description of group1.long",
|
||||
rangeInt(1L, 100L),
|
||||
0L
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new ConfigOption<>(
|
||||
"group1.long",
|
||||
"description of group1.long",
|
||||
rangeInt(1L, 100L),
|
||||
101L
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new ConfigOption<>(
|
||||
"group1.double",
|
||||
"description of group1.double",
|
||||
rangeDouble(1D, 10D),
|
||||
0D
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new ConfigOption<>(
|
||||
"group1.double",
|
||||
"description of group1.double",
|
||||
rangeDouble(1D, 10D),
|
||||
11D
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new ConfigOption<>(
|
||||
"group1.class",
|
||||
"description of group1.class",
|
||||
input -> input != null && input.equals(Long.class),
|
||||
Integer.class
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new ConfigListOption<>(
|
||||
"group1.list",
|
||||
"description of list with invalid default values",
|
||||
disallowEmpty()
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
new ConfigListOption<>(
|
||||
"group1.list",
|
||||
"description of list with invalid default values",
|
||||
null
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new ConfigListConvOption<String, WeekDay>(
|
||||
"group1.list_conv",
|
||||
"description of list_conv with invalid default values",
|
||||
disallowEmpty(),
|
||||
s -> WeekDay.valueOf(s)
|
||||
);
|
||||
});
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
new ConfigListConvOption<String, WeekDay>(
|
||||
"group1.list_conv",
|
||||
"description of list_conv with invalid default values",
|
||||
null,
|
||||
s -> WeekDay.valueOf(s)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHugeConfig() throws Exception {
|
||||
Configuration conf = new PropertiesConfiguration();
|
||||
Assert.assertEquals(DisabledListDelimiterHandler.INSTANCE,
|
||||
((AbstractConfiguration) conf).getListDelimiterHandler());
|
||||
|
||||
HugeConfig config = new HugeConfig(conf);
|
||||
|
||||
Assert.assertEquals("text1-value", config.get(TestOptions.text1));
|
||||
Assert.assertEquals("text2-value", config.get(TestOptions.text2));
|
||||
Assert.assertEquals("CHOICE-1", config.get(TestOptions.text3));
|
||||
|
||||
Assert.assertEquals(1, (int) config.get(TestOptions.int1));
|
||||
Assert.assertEquals(10, (int) config.get(TestOptions.int2));
|
||||
Assert.assertEquals(10, (int) config.get(TestOptions.int3));
|
||||
|
||||
Assert.assertEquals(100L, (long) config.get(TestOptions.long1));
|
||||
|
||||
Assert.assertEquals(100.0f, config.get(TestOptions.float1), 0f);
|
||||
Assert.assertEquals(100.0f, config.get(TestOptions.double1), 0d);
|
||||
|
||||
Assert.assertEquals(true, config.get(TestOptions.bool));
|
||||
|
||||
Assert.assertEquals(Object.class, config.get(TestOptions.clazz));
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
config.setProperty(TestOptions.clazz.name(),
|
||||
"org.apache.hugegraph.HugeGraph");
|
||||
}, e -> {
|
||||
Assert.assertTrue(e.getCause() instanceof ClassNotFoundException);
|
||||
});
|
||||
|
||||
Assert.assertEquals(Arrays.asList("list-value1", "list-value2"),
|
||||
config.get(TestOptions.list));
|
||||
|
||||
Assert.assertEquals(ImmutableMap.of("key1", "value1", "key2", "value2"),
|
||||
config.getMap(TestOptions.map));
|
||||
|
||||
Assert.assertEquals(WeekDay.WEDNESDAY, config.get(TestOptions.weekday));
|
||||
Assert.assertEquals(Arrays.asList(WeekDay.SATURDAY, WeekDay.SUNDAY),
|
||||
config.get(TestOptions.weekdays));
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new HugeConfig((Configuration) null);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHugeConfigWithFile() throws Exception {
|
||||
HugeConfig config = new HugeConfig(CONF);
|
||||
|
||||
Assert.assertEquals("file-text1-value", config.get(TestOptions.text1));
|
||||
Assert.assertEquals("file-text2-value", config.get(TestOptions.text2));
|
||||
Assert.assertEquals("CHOICE-3", config.get(TestOptions.text3));
|
||||
|
||||
Assert.assertEquals(2, (int) config.get(TestOptions.int1));
|
||||
Assert.assertEquals(0, (int) config.get(TestOptions.int2));
|
||||
Assert.assertEquals(1, (int) config.get(TestOptions.int3));
|
||||
|
||||
Assert.assertEquals(99L, (long) config.get(TestOptions.long1));
|
||||
|
||||
Assert.assertEquals(66.0f, config.get(TestOptions.float1), 0f);
|
||||
Assert.assertEquals(66.0f, config.get(TestOptions.double1), 0d);
|
||||
|
||||
Assert.assertEquals(false, config.get(TestOptions.bool));
|
||||
|
||||
Assert.assertEquals(String.class, config.get(TestOptions.clazz));
|
||||
|
||||
Assert.assertEquals(Arrays.asList("file-v1", "file-v2", "file-v3"),
|
||||
config.get(TestOptions.list));
|
||||
|
||||
Assert.assertEquals(ImmutableMap.of("key1", "value1", "key3", "value3"),
|
||||
config.getMap(TestOptions.map));
|
||||
|
||||
Assert.assertEquals(WeekDay.SUNDAY, config.get(TestOptions.weekday));
|
||||
Assert.assertEquals(Arrays.asList(WeekDay.SATURDAY, WeekDay.FRIDAY),
|
||||
config.get(TestOptions.weekdays));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHugeConfigWithConfiguration() throws Exception {
|
||||
PropertiesConfiguration configuration = new PropertiesConfiguration();
|
||||
FileHandler fileHandler = new FileHandler(configuration);
|
||||
fileHandler.load(CONF);
|
||||
HugeConfig config = new HugeConfig(configuration);
|
||||
|
||||
Assert.assertEquals("file-text1-value", config.get(TestOptions.text1));
|
||||
Assert.assertEquals("file-text2-value", config.get(TestOptions.text2));
|
||||
Assert.assertEquals("CHOICE-3", config.get(TestOptions.text3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHugeConfigWithOverride() throws Exception {
|
||||
Configuration conf = new PropertiesConfiguration();
|
||||
Assert.assertEquals(DisabledListDelimiterHandler.INSTANCE,
|
||||
((AbstractConfiguration) conf).getListDelimiterHandler());
|
||||
|
||||
HugeConfig config = new HugeConfig(conf);
|
||||
|
||||
Assert.assertEquals("text1-value", config.get(TestSubOptions.text1));
|
||||
|
||||
Assert.assertEquals("text2-value-override",
|
||||
config.get(TestSubOptions.text2));
|
||||
Assert.assertEquals("textsub-value",
|
||||
config.get(TestSubOptions.textsub));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHugeConfigWithTypeError() {
|
||||
OptionSpace.register("test-type-error",
|
||||
TestOptionsWithTypeError.class.getName());
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new HugeConfig(PATH + "test-type-error.conf");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHugeConfigWithCheckError() throws Exception {
|
||||
OptionSpace.register("test-check-error",
|
||||
TestOptionsWithCheckError.class.getName());
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
new HugeConfig(PATH + "test-check-error.conf");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHugeConfigWithListOptionError() throws Exception {
|
||||
OptionSpace.register("test-list-error",
|
||||
TestOptionsWithListError.class.getName());
|
||||
|
||||
Assert.assertThrows(IllegalStateException.class, () -> {
|
||||
new HugeConfig(PATH + "test-list-error.conf");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveHugeConfig() throws ConfigurationException,
|
||||
IOException {
|
||||
HugeConfig config = new HugeConfig(CONF);
|
||||
Assert.assertEquals("file-text1-value", config.get(TestOptions.text1));
|
||||
|
||||
File copiedFile = new File("copied.conf");
|
||||
config.save(copiedFile);
|
||||
Assert.assertTrue(copiedFile.exists());
|
||||
Assert.assertTrue(copiedFile.length() > 0);
|
||||
|
||||
try {
|
||||
HugeConfig copiedConfig = new HugeConfig(copiedFile.getPath());
|
||||
Assert.assertEquals(IteratorUtils.toList(config.getKeys()),
|
||||
IteratorUtils.toList(copiedConfig.getKeys()));
|
||||
Assert.assertEquals(config.get(TestOptions.text1),
|
||||
copiedConfig.get(TestOptions.text1));
|
||||
} finally {
|
||||
FileUtils.forceDelete(copiedFile);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromMapConfigurationWithList() {
|
||||
Map<String, String> options = new HashMap<>();
|
||||
options.put(TestOptions.list.name(), "[a, b]");
|
||||
MapConfiguration mapConfiguration = new MapConfiguration(options);
|
||||
HugeConfig hugeConfig = new HugeConfig(mapConfiguration);
|
||||
List<String> values = hugeConfig.get(TestOptions.list);
|
||||
Assert.assertEquals(2, values.size());
|
||||
Assert.assertTrue(values.contains("a"));
|
||||
Assert.assertTrue(values.contains("b"));
|
||||
}
|
||||
|
||||
public static class TestOptions extends OptionHolder {
|
||||
|
||||
private static volatile TestOptions instance;
|
||||
|
||||
public static synchronized TestOptions instance() {
|
||||
if (instance == null) {
|
||||
instance = new TestOptions();
|
||||
instance.registerOptions();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static final ConfigOption<String> text1 =
|
||||
new ConfigOption<>(
|
||||
"group1.text1",
|
||||
"description of group1.text1",
|
||||
disallowEmpty(),
|
||||
"text1-value"
|
||||
);
|
||||
|
||||
public static final ConfigOption<String> text2 =
|
||||
new ConfigOption<>(
|
||||
"group1.text2",
|
||||
"description of group1.text2",
|
||||
"text2-value"
|
||||
);
|
||||
|
||||
public static final ConfigOption<String> text3 =
|
||||
new ConfigOption<>(
|
||||
"group1.text3",
|
||||
"description of group1.text3",
|
||||
allowValues("CHOICE-1", "CHOICE-2", "CHOICE-3"),
|
||||
"CHOICE-1"
|
||||
);
|
||||
|
||||
public static final ConfigOption<Integer> int1 =
|
||||
new ConfigOption<>(
|
||||
"group1.int1",
|
||||
"description of group1.int1",
|
||||
rangeInt(1, 100),
|
||||
1
|
||||
);
|
||||
|
||||
public static final ConfigOption<Integer> int2 =
|
||||
new ConfigOption<>(
|
||||
"group1.int2",
|
||||
"description of group1.int2",
|
||||
nonNegativeInt(),
|
||||
10
|
||||
);
|
||||
|
||||
public static final ConfigOption<Integer> int3 =
|
||||
new ConfigOption<>(
|
||||
"group1.int3",
|
||||
"description of group1.int3",
|
||||
positiveInt(),
|
||||
10
|
||||
);
|
||||
|
||||
public static final ConfigOption<Long> long1 =
|
||||
new ConfigOption<>(
|
||||
"group1.long1",
|
||||
"description of group1.long1",
|
||||
rangeInt(1L, 100L),
|
||||
100L
|
||||
);
|
||||
|
||||
public static final ConfigOption<Float> float1 =
|
||||
new ConfigOption<>(
|
||||
"group1.float1",
|
||||
"description of group1.float1",
|
||||
rangeDouble(1.0f, 100.0f),
|
||||
100.0f
|
||||
);
|
||||
|
||||
public static final ConfigOption<Double> double1 =
|
||||
new ConfigOption<>(
|
||||
"group1.double1",
|
||||
"description of group1.double1",
|
||||
rangeDouble(1.0, 100.0),
|
||||
100.0
|
||||
);
|
||||
|
||||
public static final ConfigOption<Boolean> bool =
|
||||
new ConfigOption<>(
|
||||
"group1.bool",
|
||||
"description of group1.bool",
|
||||
disallowEmpty(),
|
||||
true
|
||||
);
|
||||
|
||||
public static final ConfigOption<Class<?>> clazz =
|
||||
new ConfigOption<>(
|
||||
"group1.class",
|
||||
"description of group1.class",
|
||||
disallowEmpty(),
|
||||
Object.class
|
||||
);
|
||||
|
||||
public static final ConfigConvOption<String, WeekDay> weekday =
|
||||
new ConfigConvOption<>(
|
||||
"group1.weekday",
|
||||
"description of group1.weekday",
|
||||
allowValues("SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY",
|
||||
"THURSDAY", "FRIDAY", "SATURDAY"),
|
||||
WeekDay::valueOf,
|
||||
"WEDNESDAY"
|
||||
);
|
||||
|
||||
public static final ConfigListConvOption<String, WeekDay> weekdays =
|
||||
new ConfigListConvOption<>(
|
||||
"group1.weekdays",
|
||||
"description of group1.weekdays",
|
||||
inValues("SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY",
|
||||
"THURSDAY", "FRIDAY", "SATURDAY"),
|
||||
WeekDay::valueOf,
|
||||
"SATURDAY", "SUNDAY"
|
||||
);
|
||||
|
||||
public static final ConfigListOption<String> list =
|
||||
new ConfigListOption<>(
|
||||
"group1.list",
|
||||
"description of group1.list",
|
||||
disallowEmpty(),
|
||||
"list-value1", "list-value2"
|
||||
);
|
||||
|
||||
public static final ConfigListOption<String> map =
|
||||
new ConfigListOption<>(
|
||||
"group1.map",
|
||||
"description of group1.map",
|
||||
disallowEmpty(),
|
||||
"key1:value1", "key2:value2"
|
||||
);
|
||||
}
|
||||
|
||||
public static class TestSubOptions extends TestOptions {
|
||||
|
||||
public static final ConfigOption<String> text2 =
|
||||
new ConfigOption<>(
|
||||
"group1.text2",
|
||||
true,
|
||||
"description of group1.text2 sub",
|
||||
disallowEmpty(),
|
||||
String.class,
|
||||
"text2-value-override"
|
||||
);
|
||||
|
||||
public static final ConfigOption<String> textsub =
|
||||
new ConfigOption<>(
|
||||
"group1.textsub",
|
||||
"description of group1.textsub",
|
||||
disallowEmpty(),
|
||||
"textsub-value"
|
||||
);
|
||||
}
|
||||
|
||||
public static class TestOptionsWithTypeError extends OptionHolder {
|
||||
|
||||
private static volatile TestOptionsWithTypeError instance;
|
||||
|
||||
public static synchronized TestOptionsWithTypeError instance() {
|
||||
if (instance == null) {
|
||||
instance = new TestOptionsWithTypeError();
|
||||
instance.registerOptions();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static final ConfigOption<Integer> intError =
|
||||
new ConfigOption<>(
|
||||
"group1.int_type_error",
|
||||
"description of group1.int_type_error",
|
||||
rangeInt(1, 100),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
public static class TestOptionsWithCheckError extends OptionHolder {
|
||||
|
||||
private static volatile TestOptionsWithCheckError instance;
|
||||
|
||||
public static synchronized TestOptionsWithCheckError instance() {
|
||||
if (instance == null) {
|
||||
instance = new TestOptionsWithCheckError();
|
||||
instance.registerOptions();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static final ConfigOption<Integer> intError =
|
||||
new ConfigOption<>(
|
||||
"group1.int_check_error",
|
||||
"description of group1.int_check_error",
|
||||
rangeInt(1, 100),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
public static class TestOptionsWithListError extends OptionHolder {
|
||||
|
||||
private static volatile TestOptionsWithListError instance;
|
||||
|
||||
public static synchronized TestOptionsWithListError instance() {
|
||||
if (instance == null) {
|
||||
instance = new TestOptionsWithListError();
|
||||
instance.registerOptions();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static final InvalidConfigListOption<Integer> listError =
|
||||
new InvalidConfigListOption<>(
|
||||
"group1.list_for_list_error",
|
||||
"description of group1.list_for_list_error",
|
||||
disallowEmpty(),
|
||||
1
|
||||
);
|
||||
|
||||
static class InvalidConfigListOption<T> extends ConfigOption<List<T>> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public InvalidConfigListOption(String name, String desc,
|
||||
Predicate<List<T>> pred,
|
||||
T... values) {
|
||||
super(name, false, desc, pred,
|
||||
(Class<List<T>>) Arrays.asList(values).getClass(),
|
||||
Arrays.asList(values));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean forList() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum WeekDay {
|
||||
|
||||
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.apache.hugegraph.unit.config;
|
||||
|
||||
import static org.apache.hugegraph.config.OptionChecker.disallowEmpty;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.hugegraph.config.OptionSpace;
|
||||
import org.apache.hugegraph.config.OptionHolder;
|
||||
import org.apache.hugegraph.config.ConfigException;
|
||||
import org.apache.hugegraph.config.ConfigOption;
|
||||
import org.apache.hugegraph.testutil.Assert;
|
||||
import org.apache.hugegraph.unit.BaseUnitTest;
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
public class OptionSpaceTest extends BaseUnitTest {
|
||||
|
||||
@Test
|
||||
public void tesRegister() {
|
||||
int oldSize = OptionSpace.keys().size();
|
||||
|
||||
OptionSpace.register("testgroup1", OptionHolder1.class.getName());
|
||||
Assert.assertEquals(oldSize + 2, OptionSpace.keys().size());
|
||||
Assert.assertTrue(OptionSpace.containKey("testgroup1.text1"));
|
||||
Assert.assertTrue(OptionSpace.containKey("testgroup1.text2"));
|
||||
|
||||
OptionSpace.register("testgroup1", new OptionHolder1());
|
||||
Assert.assertEquals(oldSize + 2, OptionSpace.keys().size());
|
||||
Assert.assertTrue(OptionSpace.containKey("testgroup1.text1"));
|
||||
Assert.assertTrue(OptionSpace.containKey("testgroup1.text2"));
|
||||
|
||||
OptionSpace.register("testgroup2", OptionHolder2.class.getName());
|
||||
Assert.assertEquals(oldSize + 4, OptionSpace.keys().size());
|
||||
|
||||
Assert.assertTrue(OptionSpace.containKey("testgroup1.text1"));
|
||||
Assert.assertTrue(OptionSpace.containKey("testgroup1.text2"));
|
||||
Assert.assertTrue(OptionSpace.containKey("testgroup2.text1"));
|
||||
Assert.assertTrue(OptionSpace.containKey("testgroup2.text2"));
|
||||
|
||||
Assert.assertEquals("text1 value",
|
||||
OptionSpace.get("testgroup1.text1").defaultValue());
|
||||
Assert.assertEquals("text2 value",
|
||||
OptionSpace.get("testgroup1.text2").defaultValue());
|
||||
Assert.assertEquals("text1 value",
|
||||
OptionSpace.get("testgroup2.text1").defaultValue());
|
||||
Assert.assertEquals("text2 value",
|
||||
OptionSpace.get("testgroup2.text2").defaultValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterWithError() {
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
OptionSpace.register("test-error", "fake");
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
OptionSpace.register("test-error", Exception.class.getName());
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
OptionSpace.register("test-error",
|
||||
OptionHolderWithoutInstance.class.getName());
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
OptionSpace.register("test-error", OptionHolderWithNonStaticInstance
|
||||
.class.getName());
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
OptionSpace.register("test-error",
|
||||
OptionHolderWithInstanceNull.class.getName());
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
OptionSpace.register("test-error",
|
||||
OptionHolderWithInstanceThrow.class.getName());
|
||||
});
|
||||
|
||||
Assert.assertThrows(ConfigException.class, () -> {
|
||||
OptionSpace.register("test-error",
|
||||
OptionHolderWithInvalidOption.class.getName());
|
||||
});
|
||||
}
|
||||
|
||||
public static class OptionHolderWithoutInstance extends OptionHolder {
|
||||
// no instance()
|
||||
}
|
||||
|
||||
public static class OptionHolderWithNonStaticInstance extends OptionHolder {
|
||||
|
||||
// not static instance()
|
||||
public OptionHolderWithNonStaticInstance instance() {
|
||||
return new OptionHolderWithNonStaticInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public static class OptionHolderWithInstanceNull extends OptionHolder {
|
||||
|
||||
public static OptionHolderWithInstanceNull instance() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class OptionHolderWithInstanceThrow extends OptionHolder {
|
||||
|
||||
public static OptionHolderWithInstanceNull instance() {
|
||||
throw new RuntimeException("test error");
|
||||
}
|
||||
}
|
||||
|
||||
public static class OptionHolderWithInvalidOption extends OptionHolder {
|
||||
|
||||
public static OptionHolderWithInvalidOption instance() {
|
||||
return new OptionHolderWithInvalidOption();
|
||||
}
|
||||
|
||||
private OptionHolderWithInvalidOption() {
|
||||
this.registerOptions();
|
||||
}
|
||||
|
||||
public static final String fake = "fake";
|
||||
|
||||
public static final ConfigOption<String> invalid =
|
||||
new InvalidOption<>(
|
||||
"group1.text1",
|
||||
"description of group1.text1",
|
||||
disallowEmpty(),
|
||||
"value"
|
||||
);
|
||||
|
||||
public static class InvalidOption<T> extends ConfigOption<T> {
|
||||
|
||||
public InvalidOption(String name, String desc,
|
||||
Predicate<T> pred, T value) {
|
||||
super(name, desc, pred, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
throw new RuntimeException("fake");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class OptionHolder1 extends OptionHolder {
|
||||
|
||||
public static OptionHolder1 instance() {
|
||||
return new OptionHolder1();
|
||||
}
|
||||
|
||||
OptionHolder1() {
|
||||
this.registerOptions();
|
||||
}
|
||||
|
||||
public static final ConfigOption<String> text1 =
|
||||
new ConfigOption<>(
|
||||
"testgroup1.text1",
|
||||
"description of testgroup1.text1",
|
||||
disallowEmpty(),
|
||||
"text1 value"
|
||||
);
|
||||
|
||||
public static final ConfigOption<String> text2 =
|
||||
new ConfigOption<>(
|
||||
"testgroup1.text2",
|
||||
"description of testgroup1.text2",
|
||||
disallowEmpty(),
|
||||
"text2 value"
|
||||
);
|
||||
}
|
||||
|
||||
public static class OptionHolder2 extends OptionHolder {
|
||||
|
||||
public static OptionHolder2 instance() {
|
||||
return new OptionHolder2();
|
||||
}
|
||||
|
||||
OptionHolder2() {
|
||||
this.registerOptions();
|
||||
}
|
||||
|
||||
public static final ConfigOption<String> text1 =
|
||||
new ConfigOption<>(
|
||||
"testgroup2.text1",
|
||||
"description of testgroup2.text1",
|
||||
disallowEmpty(),
|
||||
"text1 value"
|
||||
);
|
||||
|
||||
public static final ConfigOption<String> text2 =
|
||||
new ConfigOption<>(
|
||||
"testgroup2.text2",
|
||||
"description of testgroup2.text2",
|
||||
disallowEmpty(),
|
||||
"text2 value"
|
||||
);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue