Skip to content

Commit

Permalink
usages-3.0-alpha-1
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikita Koval committed Oct 21, 2017
0 parents commit 6268518
Show file tree
Hide file tree
Showing 94 changed files with 9,220 additions and 0 deletions.
47 changes: 47 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#idea files
.idea/
*.iml
*.ipr
*.iws
atlassian-ide-plugin.xml
out/

# java
*.class
*.jar
*.war
*.ear

# maven specific
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties

#gradle
.gradle
build/

# Ignore Gradle GUI config
gradle-app.setting

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

# Cache of project
.gradletasknamecache

# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties

# common
*~
*.bak
.DS_Store
.DS_Store?
._*
15 changes: 15 additions & 0 deletions HEADER.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Copyright (C) ${year} ${name}

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public
License along with this program. If not, see
<http://www.gnu.org/licenses/gpl-3.0.html>.
674 changes: 674 additions & 0 deletions LICENSE.txt

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
Usages Analysis Tool
======

This tool finds code usages in the specified Maven repositories. It indexes repositories, downloads required artifacts and scans ".class" files in them. The tool analyzes all kinds of dependencies: usages of fields, usages of methods, extensions of classes and implementation of interfaces, usages of annotations, overrides of methods.

The tool is separated into 2 parts: server application, which collects all information and analyzes classes, and a client one, which is implemented as IntelliJ Plugin. Both of them are currently in development, but alpha versions are available on our portal.
[https://code.devexperts.com/display/USAGES/About+Usages]()

Server
------
Server part of the tool is a Java application and could be run simply:

`java -jar server-${version}.jar`


### Configuration properties
All parameters are passed as system variables (`-D<name>=<value>`)

* **server.port** - defines server port, which is used for find usages requests, *8080* by default;
* **usages.workDir** - defines working directory for *settings.xml* (see section below) and work files (e.g. caches, database), *~/usages/* by default.

### Repositories indexing configuration
You need to provide information about your repositories in `${usages.workDir}/settings.xml` file. See the example below.

```xml
<settings>
<!-- Repositories to be indexed>
<repositories>
<repository>
<id>qd</id>
<url>https://maven.in.devexperts.com/content/repositories/qd/</url>
<!-- repository system, "nexus" or "artifactory" -->
<type>nexus</type>
<user>username</user>
<password>password</password>
<!-- scan repository for new artifacts every 3 hours ->
<scanTimePeriod>3h</scanTimePeriod>
</repository>
<!-- Type of artifacts to be analyzed -->
<artifactTypes>
<type>jar</type>
<type>war</type>
</artifactTypes>
</settings>
```

IntelliJ Plugin
---------------
Plugin for IntelliJ IDEA should be installed from disk using `idea-plugin-${version}.jar`. After the installation, set URL to usages server in plugin configuration (**Tools --> Configure Usages plugin**). To perform find usages action use **CTRL + F9** (for simple search) or **ALT + CTRL + SHIFT + F9** (with configuration before the search) shortcut when you are on the element to be searched for.
15 changes: 15 additions & 0 deletions aether-shaded/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
dependencies {
compile "org.apache.maven:maven-aether-provider:3.3.9"
compile "org.eclipse.aether:aether-api:$aether_version"
compile "org.eclipse.aether:aether-util:$aether_version"
compile "org.eclipse.aether:aether-transport-file:$aether_version"
compile "org.eclipse.aether:aether-transport-http:$aether_version"
compile "org.eclipse.aether:aether-connector-basic:$aether_version"
}

apply plugin: 'com.github.johnrengelman.shadow'

shadowJar {
relocate 'com.google', 'com.devexperts.usages.shaded.com.google'
relocate 'org.objectweb.asm', 'com.devexperts.usages.shaded.org.objectweb.asm'
}
7 changes: 7 additions & 0 deletions api/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
description = 'Internal API'
dependencies {
// compile group: 'org.springframework', name: 'spring-web', version: '4.3.2.RELEASE'
compile "org.springframework.boot:spring-boot-starter-webflux:$spring_boot_version"
compile group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.7.2'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.2'
}
186 changes: 186 additions & 0 deletions api/src/main/kotlin/com/devexperts/usages/api/Api.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/**
* Copyright (C) 2017 Devexperts LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
*/
package com.devexperts.usages.api

import com.fasterxml.jackson.annotation.JsonProperty
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.web.reactive.function.BodyInserters
import org.springframework.web.reactive.function.client.WebClient
import java.lang.Integer.parseInt
import java.util.*

const val UUID_HEADER_NAME = "UUID"

/**
* It is used to do [MemberUsageRequest] and process responses in stream format.
* Note that all methods can be invoked concurrently, from different threads.
*/
abstract class MemberUsageRequestProcessor(val serverUrls: List<String>,
val memberUsagesRequest: MemberUsageRequest) {
private val uuid = UUID.randomUUID().toString()
private @Volatile
var cancelled = false
abstract fun onNewUsages(serverUrl: String, usages: List<MemberUsage>)
abstract fun onError(serverUrl: String, message: String, throwable: Throwable?)
abstract fun onComplete()

fun doRequest() {
serverUrls.forEach { url ->
try {
val webClient = WebClient.builder()
.baseUrl(url)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_STREAM_JSON_VALUE)
.defaultHeader(UUID_HEADER_NAME, uuid)
.build()
val usagesFlux = webClient.post()
.uri("/usages")
.body(BodyInserters.fromObject(memberUsagesRequest))
.exchange().flatMapMany { it.bodyToFlux(MemberUsage::class.java) }
usagesFlux.toStream().forEach{ onNewUsages(url, listOf(it)) }
// val block = CountDownLatch(1)
// usagesFlux.doFinally { block.countDown() }
// block.await()
// usagesFlux.subscribe(object : Subscriber<MemberUsage> {
// override fun onNext(usage: MemberUsage) {
// onNewUsages(url, listOf(usage))
// }
//
// override fun onSubscribe(s: Subscription) {
// subscriptions += s
// }
//
// override fun onError(t: Throwable) {
// block.countDown()
// onError(url, "Error during request to $url", t)
// }
//
// override fun onComplete() {
// block.countDown()
// }
// })
// block.await()
} catch (e: Throwable) {
onError(url, "Error during request to $url", e)
}
}
onComplete()
}

fun cancel() {
cancelled = true
}
}

enum class CompleteMessage {
COMPLETE, USAGES_NUMBER_EXCEED
}


/**
* Parses artifact mask which should be in one of the following formats:
* * `<groupId>:<artifactId>:<version>`
* * `<groupId>:<artifactId>:<version>:<numberOfLastVersions>`
* * `<groupId>:<artifactId>:<packaging>:<classifier>:<version>:<numberOfLastVersions>`
*/
fun createArtifactMaskFromString(config: String): ArtifactMask {
val props = config.trim().split(":")
if (props.size == 3) {
// <groupId>:<artifactId>:<version>
return ArtifactMask(groupId = props[0], artifactId = props[1], version = props[2])
} else if (props.size == 4) {
// <groupId>:<artifactId>:<version>:<numberOfLastVersions>
return ArtifactMask(groupId = props[0], artifactId = props[1], version = props[2],
numberOfLastVersions = parseInt(props[3]))
} else if (props.size == 6) {
// <groupId>:<artifactId>:<packaging>:<classifier>:<version>:<numberOfLastVersions>
return ArtifactMask(groupId = props[0], artifactId = props[1], packaging = props[2],
classifier = props[3], version = props[4], numberOfLastVersions = parseInt(props[5]))
} else {
throw IllegalArgumentException("Invalid configuration: " + config)
}
}

const val ANY = "*"

data class ArtifactMask(
@JsonProperty("groupId") val groupId: String = ANY,
@JsonProperty("artifactId") val artifactId: String = ANY,
@JsonProperty("packaging") val packaging: String = ANY,
@JsonProperty("classifier") val classifier: String = ANY,
@JsonProperty("version") val version: String = ANY,
@JsonProperty("numberOfLastVersions") val numberOfLastVersions: Int = 1
)

data class MemberUsageRequest(
@JsonProperty("member") val member: Member, // find usages of this member
@JsonProperty("searchScope") val searchScope: ArtifactMask, // search in these artifacts only
@JsonProperty("findClasses") val findClasses: Boolean, // find usages of classes from specified package (as member)
@JsonProperty("findMethods") val findMethods: Boolean, // find method usages during finding usages of the class
@JsonProperty("findFields") val findFields: Boolean,
// @JsonProperty("findBaseClassesUsages") val findBaseClassesUsages: Boolean,
@JsonProperty("findDerivedClassesUsages") val findDerivedClassesUsages: Boolean,
// @JsonProperty("findBaseMethodsUsages") val findBaseMethodsUsages: Boolean,
@JsonProperty("findDerivedMethodsUsages") val findDerivedMethodsUsages: Boolean
)


val usages = arrayListOf(
MemberUsage(
member = Member("com.devexperts.util.TimePeriod", emptyList(), MemberType.CLASS),
usageKind = UsageKind.FIELD, // property type
location = Location(
artifact = Artifact("com.devexperts.usages", "server", "3.0", null, null),
member = Member("com.devexperts.usages.config.RepositorySettings", emptyList(), MemberType.CLASS),
file = "com/devexperts/usages/config/Settings.kt", lineNumber = 54)),
MemberUsage(
member = Member("com.devexperts.util.TimePeriod", emptyList(), MemberType.CLASS),
usageKind = UsageKind.METHOD_RETURN, // method return type
location = Location(
artifact = Artifact("com.devexperts.usages", "server", "3.0", null, null),
member = Member("com.devexperts.usages.config.TimePeriodConverter#myMethod", emptyList(), MemberType.METHOD),
file = "com/devexperts/usages/config/Settings.kt", lineNumber = 76)),
MemberUsage(
member = Member("com.devexperts.util.TimePeriod", emptyList(), MemberType.CLASS),
usageKind = UsageKind.METHOD_RETURN, // nested class/object
location = Location(
artifact = Artifact("com.devexperts.usages", "server", "3.0", null, null),
member = Member("com.devexperts.usages.config.TimePeriodConverter", emptyList(), MemberType.CLASS),
file = "com/devexperts/usages/config/Settings.kt", lineNumber = 77)),
MemberUsage(
member = Member("com.devexperts.util.TimePeriod", emptyList(), MemberType.CLASS),
usageKind = UsageKind.METHOD_RETURN, // nested class/object
location = Location(
artifact = Artifact("com.devexperts.usages", "server", "3.0", null, null),
member = Member("com.devexperts.usages.config.RepositorySettings", emptyList(), MemberType.CLASS),
file = "com/devexperts/usages/config/Settings.kt", lineNumber = 54)),
MemberUsage(
member = Member("com.devexperts.util.IndexedSet", emptyList(), MemberType.CLASS),
usageKind = UsageKind.NEW,
location = Location(
artifact = Artifact("com.devexperts.qd", "qd-core", "3.257", null, null),
member = Member("com.devexperts.qd.core.MyIndexedSet", emptyList(), MemberType.CLASS),
file = "com/devexperts/qd/core/MyIndexedSet", lineNumber = 47)),
MemberUsage(
member = Member("com.devexperts.util.IndexedSet", emptyList(), MemberType.CLASS),
usageKind = UsageKind.FIELD,
location = Location(
artifact = Artifact("com.devexperts.qd", "qd-core", "3.256", null, null),
member = Member("com.devexperts.qd.core.MyIndexedSet", emptyList(), MemberType.CLASS),
file = "com/devexperts/qd/core/MyIndexedSet", lineNumber = 35))
)
Loading

0 comments on commit 6268518

Please sign in to comment.