KT-11261 Implement MavenArchetypesProvider extension point

This commit is contained in:
Sergey Mashkov
2016-03-17 15:30:10 +03:00
parent 14ca0f4aac
commit cb4702c406
4 changed files with 290 additions and 0 deletions
+1
View File
@@ -5,6 +5,7 @@
</extensions>
<extensions defaultExtensionNs="org.jetbrains.idea.maven">
<importer implementation="org.jetbrains.kotlin.idea.configuration.KotlinMavenImporter" />
<archetypesProvider implementation="org.jetbrains.kotlin.idea.configuration.KotlinMavenArchetypesProvider" />
</extensions>
<module-components>
<component>
@@ -0,0 +1,101 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.configuration
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import com.intellij.util.net.HttpConfigurable
import org.jetbrains.idea.maven.dom.MavenVersionComparable
import org.jetbrains.idea.maven.indices.MavenArchetypesProvider
import org.jetbrains.idea.maven.model.MavenArchetype
import org.jetbrains.kotlin.idea.KotlinPluginUtil
import org.jetbrains.kotlin.utils.ifEmpty
import java.net.HttpURLConnection
import java.net.URLEncoder
import java.util.concurrent.TimeUnit
class KotlinMavenArchetypesProvider(val kotlinPluginVersion: String) : MavenArchetypesProvider {
constructor() : this(KotlinPluginUtil.getPluginVersion())
val VERSIONS_LIST_URL = mavenSearchUrl("org.jetbrains.kotlin", packaging = "maven-archetype", rowsLimit = 1000)
private val versionPrefix by lazy { """^\d+\.\d+\.""".toRegex().find(kotlinPluginVersion)?.value ?: "1.0." }
private val archetypesBlocking by lazy {
try {
loadVersions().ifEmpty { defaultArchetypes() }
} catch (t: Throwable) {
defaultArchetypes()
}
}
override fun getArchetypes() = archetypesBlocking.toMutableList()
private fun defaultArchetypes() = listOf(MavenArchetype("org.jetbrains.kotlin", "kotlin-archetype-jvm", "1.0.0", null, null))
private fun loadVersions(): List<MavenArchetype> {
return connectAndApply(VERSIONS_LIST_URL) { urlConnection ->
urlConnection.inputStream.bufferedReader().use { reader ->
extractVersions(JsonParser().parse(reader))
}
}
}
internal fun extractVersions(root: JsonElement) =
root.asJsonObject.get("response")
.asJsonObject.get("docs")
.asJsonArray
.map { it.asJsonObject }
.map { MavenArchetype(it.get("g").asString, it.get("a").asString, it.get("v").asString, null, null) }
.filter { it.version?.startsWith(versionPrefix) ?: false }
.maxBy { MavenVersionComparable(it.version) }
.let { listOfNotNull(it) }
private fun mavenSearchUrl(group: String, artifactId: String? = null, version: String? = null, packaging: String? = null, rowsLimit: Int = 20): String {
val q = listOf(
"g" to group,
"a" to artifactId,
"v" to version,
"p" to packaging
)
.filter { it.second != null }
.map { "${it.first}:\"${it.second}\"" }
.joinToString(separator = " AND ")
return "http://search.maven.org/solrsearch/select?q=${q.encodeURL()}&core=gav&rows=$rowsLimit&wt=json"
}
private fun <R> connectAndApply(url: String, timeoutSeconds: Int = 15, block: (HttpURLConnection) -> R): R {
return HttpConfigurable.getInstance().openHttpConnection(url).use { urlConnection ->
val timeout = TimeUnit.SECONDS.toMillis(timeoutSeconds.toLong()).toInt()
urlConnection.connectTimeout = timeout
urlConnection.readTimeout = timeout
urlConnection.connect()
block(urlConnection)
}
}
private fun <R> HttpURLConnection.use(block: (HttpURLConnection) -> R): R =
try {
block(this)
}
finally {
disconnect()
}
private fun String.encodeURL() = URLEncoder.encode(this, "UTF-8")
}
@@ -0,0 +1,122 @@
{
"responseHeader": {
"status": 0,
"QTime": 2,
"params": {
"fl": "id,g,a,v,p,ec,timestamp,tags",
"sort": "score desc,timestamp desc,g asc,a asc,v desc",
"indent": "off",
"q": "g:\"org.jetbrains.kotlin\" AND p:\"maven-archetype\"",
"core": "gav",
"wt": "json",
"rows": "200",
"version": "2.2"
}
},
"response": {
"numFound": 3,
"start": 0,
"docs": [
{
"id": "org.jetbrains.kotlin:kotlin-archetype-jvm:1.0.0",
"g": "org.jetbrains.kotlin",
"a": "kotlin-archetype-jvm",
"v": "1.0.0",
"p": "maven-archetype",
"timestamp": 1458127798000,
"tags": [
"using",
"restful",
"creates",
"example",
"webservice"
],
"ec": [
"-sources.jar",
".jar",
".pom"
]
},
{
"id": "org.jetbrains.kotlin:kotlin-archetype-js:1.0.0",
"g": "org.jetbrains.kotlin",
"a": "kotlin-archetype-js",
"v": "1.0.0",
"p": "maven-archetype",
"timestamp": 1458127798000,
"tags": [
"using",
"restful",
"creates",
"example",
"webservice"
],
"ec": [
"-sources.jar",
".jar",
".pom"
]
},
{
"id": "org.jetbrains.kotlin:kotlin-archetype-jvm:1.0.1",
"g": "org.jetbrains.kotlin",
"a": "kotlin-archetype-jvm",
"v": "1.0.1",
"p": "maven-archetype",
"timestamp": 1458127798000,
"tags": [
"using",
"restful",
"creates",
"example",
"webservice"
],
"ec": [
"-sources.jar",
".jar",
".pom"
]
},
{
"id": "org.jetbrains.kotlin:kotlin-archetype-jvm:1.0.1",
"g": "org.jetbrains.kotlin",
"a": "kotlin-archetype-jvm",
"v": "1.0.1-2",
"p": "maven-archetype",
"timestamp": 1458127798000,
"tags": [
"using",
"restful",
"creates",
"example",
"webservice"
],
"ec": [
"-sources.jar",
".jar",
".pom"
]
},
{
"id": "org.jetbrains.kotlin:kotlin-archetype-jvm:1.1.2",
"g": "org.jetbrains.kotlin",
"a": "kotlin-archetype-jvm",
"v": "1.1.2",
"p": "maven-archetype",
"timestamp": 1458127798000,
"tags": [
"using",
"restful",
"creates",
"example",
"webservice"
],
"ec": [
"-sources.jar",
".jar",
".pom"
]
}
]
}
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.configuration
import com.google.gson.JsonParser
import org.jetbrains.idea.maven.model.MavenArchetype
import org.junit.Test
import org.junit.Assert.*
import java.io.File
class KotlinMavenArchetypesProviderTest {
private val BASE_PATH = "idea/testData/configuration/"
@Test
fun extractVersions() {
val file = File(BASE_PATH, "extractVersions/maven-central-response.json")
assertTrue("Test data is missing", file.exists())
val json = file.bufferedReader().use {
JsonParser().parse(it)
}
val versions = KotlinMavenArchetypesProvider("1.0.0-Release-Something-1886").extractVersions(json)
assertEquals(
listOf(
MavenArchetype("org.jetbrains.kotlin", "kotlin-archetype-jvm", "1.0.1-2", null, null)
).sortedBy { it.artifactId + "." + it.version },
versions.sortedBy { it.artifactId + "." + it.version }
)
}
@Test
fun extractVersionsNewPlugin() {
val file = File(BASE_PATH, "extractVersions/maven-central-response.json")
assertTrue("Test data is missing", file.exists())
val json = file.bufferedReader().use {
JsonParser().parse(it)
}
val versions = KotlinMavenArchetypesProvider("1.1.0-Next-Release-Something-9999").extractVersions(json)
assertEquals(
listOf(
MavenArchetype("org.jetbrains.kotlin", "kotlin-archetype-jvm", "1.1.2", null, null)
).sortedBy { it.artifactId + "." + it.version },
versions.sortedBy { it.artifactId + "." + it.version }
)
}
}