summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.txt3
-rw-r--r--doc/note/maven/base.xml (renamed from doc/note/maven-pom/base.xml)0
-rw-r--r--doc/note/maven/maven.txt5
-rw-r--r--src/main/lua/maven/MvnCentralDepScan.lua1217
4 files changed, 962 insertions, 263 deletions
diff --git a/README.txt b/README.txt
index 8064101..3b6ee8c 100644
--- a/README.txt
+++ b/README.txt
@@ -4,6 +4,3 @@ Unspecified Garbage
Just some random garbage which was handy in some way somewhen.
-Not yet migrated scripts see "C:/Users/fankhauseand/OneDrive - POSTCHAG/doc"
-
-
diff --git a/doc/note/maven-pom/base.xml b/doc/note/maven/base.xml
index 7218e99..7218e99 100644
--- a/doc/note/maven-pom/base.xml
+++ b/doc/note/maven/base.xml
diff --git a/doc/note/maven/maven.txt b/doc/note/maven/maven.txt
new file mode 100644
index 0000000..fe641e8
--- /dev/null
+++ b/doc/note/maven/maven.txt
@@ -0,0 +1,5 @@
+
+## Print effective-pom
+
+ mvn help:effective-pom -Doutput="the-effective-pom.xml"
+
diff --git a/src/main/lua/maven/MvnCentralDepScan.lua b/src/main/lua/maven/MvnCentralDepScan.lua
index 0ea449f..a1f9418 100644
--- a/src/main/lua/maven/MvnCentralDepScan.lua
+++ b/src/main/lua/maven/MvnCentralDepScan.lua
@@ -2,20 +2,19 @@
Initially written using scriptlee 0.0.5-46-G .
+ Begun experimenting with scriptlee 0.0.5-55-G but there's a stack overflow
+ bug in the XML parser somewhere.
+
]====================================================================]
-local AF_INET = require('scriptlee').posix.AF_INET
-local AF_INET6 = require('scriptlee').posix.AF_INET6
-local IPPROTO_TCP = require('scriptlee').posix.IPPROTO_TCP
-local SOCK_STREAM = require('scriptlee').posix.SOCK_STREAM
-local inaddrOfHostname = require('scriptlee').posix.inaddrOfHostname
+--local newCond = require("scriptlee").posix.newCond -- cannot use. Too buggy :(
+local newCsvRecrdInStream = require("scriptlee").newCsvRecrdInStream
local newHttpClient = require("scriptlee").newHttpClient
local newSqlite = require("scriptlee").newSqlite
-local newTlsClient = assert(require("scriptlee").newTlsClient)
+local newTlsClient = require("scriptlee").newTlsClient
local newXmlParser = require("scriptlee").newXmlParser
local objectSeal = require("scriptlee").objectSeal
local sleep = require("scriptlee").posix.sleep
-local socket = require('scriptlee').posix.socket
local startOrExecute = require("scriptlee").reactor.startOrExecute
local out, log = io.stdout, io.stderr
@@ -28,71 +27,205 @@ function mod.printHelp()
.."\n"
.." Options:\n"
.."\n"
- .." --example\n"
- .." WARN: only use if you know what you're doing!\n"
+ .." --state <path>\n"
+ .." Data file to use for the action. Will be created if it does not\n"
+ .." yet exist.\n"
+ .."\n"
+ .." --asCsv <what>\n"
+ .." Exports requested data to stdout. <what> can be one of \"parents\"\n"
+ .." or \"deps\".\n"
+ .."\n"
+ .." --nullvalue <str> (default is an empty string)\n"
+ .." The string to use for NULL values in CSV exports.\n"
+ .."\n"
+ .." --uripat <str>\n"
+ .." URI pattern where the poms can be downloaded from. Use\n"
+ .." placeholders in curly braces to tell where to put misc parts.\n"
+ .." Available placeholders are: {aid}, {gid}, {gidWithSlashes} and\n"
+ .." {version}. Placeholders can be used multiple times. Example:\n"
+ .." http://example.com/repo/{gid}/{aid}/{aid}-{version}-pom.xml\n"
+ .."\n"
+ .." --csvToFetch\n"
+ .." CSV listing the artifacts which should be merged into the state\n"
+ .." file. Example:\n"
+ .."\n"
+ .." c;groupId;artifactId;version\n"
+ .." r;com.example;foo-toolz;1.2.3\n"
+ .."\n"
+ .."\n"
+ .." Example \"Export parents\"\n"
.."\n"
- .." --sqliteOut <path>\n"
- .." Path where to export the result.\n"
+ .." --state theFile --asCsv parents > parents.csv\n"
.."\n"
+ .." Example \"Export dependencies\"\n"
+ .."\n"
+ .." --state theFile --asCsv deps > dependencies.csv\n"
+ .."\n"
+ .." Example \"Fetch info from listed POMs and merge them into state file\"\n"
+ .."\n"
+ .." --state theFile --csvToFetch the.csv --uripat http://example.com/{aid}.xml\n"
.."\n")
end
function mod.parseArgs( app )
local iA = 0
- local isExample = false
-::nextArg::
- iA = iA + 1
- local arg = _ENV.arg[iA]
- if not arg then
- goto endOfArgs
- elseif arg == "--help" then
- mod.printHelp() return -1
- elseif arg == "--example" then
- isExample = true
- elseif arg == "--sqliteOut" then
+ app.statePath = false
+ app.nullvalue = ""
+ while true do
iA = iA + 1
- arg = _ENV.arg[iA]
- if not arg then log:write("Arg --sqliteOut needs value\n")return-1 end
- app.sqliteOutFile = arg
- else
- log:write("Unexpected arg: "..tostring(arg).."\n")return -1
+ local arg = _ENV.arg[iA]
+ if not arg then
+ break
+ elseif arg == "--help" then
+ mod.printHelp() return -1
+ elseif arg == "--state" then
+ iA = iA + 1
+ arg = _ENV.arg[iA]
+ if not arg then log:write("Arg --sqliteOut needs value\n")return-1 end
+ app.statePath = arg
+ elseif arg == "--asCsv" then
+ iA = iA +1
+ arg = _ENV.arg[iA]
+ if arg ~= "parents" and arg ~= "deps" then
+ log:write("Illegal value for --asCsv: "..tostring(arg).."\n")return-1 end
+ app.asCsv = arg
+ elseif arg == "--nullvalue" then
+ iA = iA +1
+ arg = _ENV.arg[iA]
+ if not arg then log:write("Arg --nullvalue needs value\n")return-1 end
+ app.nullvalue = arg
+ elseif arg == "--uripat" then
+ iA = iA +1
+ arg = _ENV.arg[iA]
+ if not arg then log:write("Arg --uripat needs value\n")return-1 end
+ app.uripat = arg
+ elseif arg == "--csvToFetch" then
+ iA = iA +1
+ arg = _ENV.arg[iA]
+ if not arg then log:write("Arg --csvToFetch needs value\n")return-1 end
+ app.csvToFetch = arg
+ else
+ log:write("Unexpected arg: "..tostring(arg).."\n")return -1
+ end
end
- goto nextArg
-::endOfArgs::
- if not isExample then log:write("Bad Args\n") return -1 end
+ if not app.statePath then log:write("Arg --state missing\n") return -1 end
+ if not app.csvToFetch and not app.asCsv then log:write("Bad Args\n") return -1 end
+ if app.csvToFetch and not app.uripat then log:write("Arg --uripat missing\n") return -1 end
return 0
end
+function mod.newMvnArtifact()
+ return objectSeal{
+ dbId = false,
+ parentGroupId = false,
+ parentArtifactId = false,
+ parentVersion = false,
+ groupId = false,
+ artifactId = false,
+ version = false,
+ }
+end
+
+
+function mod.newMvnDependency()
+ return objectSeal{
+ dbId = false,
+ groupId = false,
+ artifactId = false,
+ version = false,
+ }
+end
+
+
function mod.newPomUrlSrc( app )
- local urls = {
- -- TODO insert URLs here!
+ local t = objectSeal{
+ app = app,
+ remainingArtifacts = false,
}
local m = {
- nextPomUrl = function(t)
- return table.remove(urls, 1)
+ nextPomArtifact = function( t )
+ if not t.remainingArtifacts then
+ local csvParser = newCsvRecrdInStream{
+ cls = t,
+ delimCol = ";",
+ onRecord = function( recrd, t )
+ local recrdType = recrd[1]
+ if recrdType == "r" then
+ local artif = mod.newMvnArtifact()
+ artif.groupId = assert(recrd[2])
+ artif.artifactId = assert(recrd[3])
+ artif.version = assert(recrd[4])
+ table.insert(t.remainingArtifacts, artif)
+ elseif recrdType == "h" or recrdType == "t" then
+ log:write("CSV")
+ for i=1, #recrd do log:write(" ".. recrd[i]) end
+ log:write("\n")
+ elseif recrdType == "c" then
+ assert(recrd[2] == "groupId")
+ assert(recrd[3] == "artifactId")
+ assert(recrd[4] == "version")
+ else
+ print("Record:")
+ for iCol, val in ipairs(recrd) do print(" -> ", iCol, val) end
+ error("TODO_20230127110829")
+ end
+ end,
+ }
+ local fd = io.open(t.app.csvToFetch, "rb")
+ if not fd then error("fopen("..tostring(t.app.csvToFetch)..")") end
+ t.remainingArtifacts = {}
+ while true do
+ local buf = fd:read(1<<14)
+ if buf then
+ csvParser:write(buf)
+ else
+ fd:close()
+ csvParser:closeSnk()
+ break
+ end
+ end
+ end
+ return table.remove(t.remainingArtifacts)
end,
__index = false,
}
m.__index = m
- return setmetatable({}, m)
+ return setmetatable(t, m)
+end
+
+
+function mod.pomFilepathByArtifact( app, pomArtifact )
+ local a = pomArtifact
+ return assert(os.getenv("HOME")) .."/.m2/repository"
+ .."/".. a.groupId:gsub("%.", "/") .."/".. a.artifactId .."/".. a.version
+ .."/".. a.artifactId .."-".. a.version ..".pom"
+end
+
+
+function mod.urlByArtifact(app, artifact)
+ local a = artifact
+ assert(type(a.artifactId) == "string", tostring(a.artifactId))
+ assert(type(a.groupId) == "string", tostring(a.groupId))
+ assert(type(a.version) == "string", tostring(a.version))
+ local url = assert(app.uripat)
+ url = url:gsub("{aid}", a.artifactId)
+ url = url:gsub("{gid}", a.groupId)
+ url = url:gsub("{gidWithSlashes}", a.groupId:gsub("%.", "/"))
+ url = url:gsub("{version}", a.version)
+ return url
end
function mod.processXmlValue( pomParser )
- local app = pomParser.req.app
+ local app = pomParser.app
local xpath = ""
for i, stackElem in ipairs(pomParser.xmlElemStack) do
xpath = xpath .."/".. stackElem.tag
end
--log:write(xpath .."\n")
local mvnArtifact = pomParser.mvnArtifact
- local newMvnDependency = function()return objectSeal{
- groupId = false,
- artifactId = false,
- version = false,
- }end
if false then
elseif xpath == "/project/parent/artifactId" then
mvnArtifact.parentArtifactId = pomParser.currentValue
@@ -107,13 +240,13 @@ function mod.processXmlValue( pomParser )
elseif xpath == "/project/version" then
mvnArtifact.version = pomParser.currentValue
elseif xpath == "/project/dependencies/dependency/groupId" then
- if not pomParser.mvnDependency then pomParser.mvnDependency = newMvnDependency() end
+ if not pomParser.mvnDependency then pomParser.mvnDependency = mod.newMvnDependency() end
pomParser.mvnDependency.groupId = pomParser.currentValue
elseif xpath == "/project/dependencies/dependency/artifactId" then
- if not pomParser.mvnDependency then pomParser.mvnDependency = newMvnDependency() end
+ if not pomParser.mvnDependency then pomParser.mvnDependency = mod.newMvnDependency() end
pomParser.mvnDependency.artifactId = pomParser.currentValue
elseif xpath == "/project/dependencies/dependency/version" then
- if not pomParser.mvnDependency then pomParser.mvnDependency = newMvnDependency() end
+ if not pomParser.mvnDependency then pomParser.mvnDependency = mod.newMvnDependency() end
pomParser.mvnDependency.version = pomParser.currentValue
elseif xpath == "/project/dependencies/dependency" then
assert(pomParser.mvnDependency)
@@ -124,13 +257,13 @@ function mod.processXmlValue( pomParser )
if not deps then deps = {} app.mvnDepsByArtifact[mvnArtifact] = deps end
table.insert(deps, assert(mvnDependency))
elseif xpath == "/project/dependencyManagement/dependencies/dependency/groupId" then
- if not pomParser.mvnMngdDependency then pomParser.mvnMngdDependency = newMvnDependency() end
+ if not pomParser.mvnMngdDependency then pomParser.mvnMngdDependency = mod.newMvnDependency() end
pomParser.mvnMngdDependency.groupId = pomParser.currentValue
elseif xpath == "/project/dependencyManagement/dependencies/dependency/artifactId" then
- if not pomParser.mvnMngdDependency then pomParser.mvnMngdDependency = newMvnDependency() end
+ if not pomParser.mvnMngdDependency then pomParser.mvnMngdDependency = mod.newMvnDependency() end
pomParser.mvnMngdDependency.artifactId = pomParser.currentValue
elseif xpath == "/project/dependencyManagement/dependencies/dependency/version" then
- if not pomParser.mvnMngdDependency then pomParser.mvnMngdDependency = newMvnDependency() end
+ if not pomParser.mvnMngdDependency then pomParser.mvnMngdDependency = mod.newMvnDependency() end
pomParser.mvnMngdDependency.version = pomParser.currentValue
elseif xpath == "/project/dependencyManagement/dependencies/dependency" then
assert(pomParser.mvnMngdDependency)
@@ -154,40 +287,39 @@ end
function mod.getMvnArtifactKey( mvnArtifact )
- assert(mvnArtifact.artifactId)
- assert(mvnArtifact.groupId)
- assert(mvnArtifact.version)
+ if type(mvnArtifact.artifactId) ~= "string" then error(tostring(mvnArtifact.artifactId))end
+ if type(mvnArtifact.groupId) ~= "string" then error(tostring(mvnArtifact.groupId))end
+ local version = mvnArtifact.version
+ local isVersionOk = (type(version) == "string")
+ if not isVersionOk then warn("Bad version: "..mvnArtifact.groupId.." "..mvnArtifact.artifactId
+ .." " ..tostring(version)) end
return mvnArtifact.groupId
.."\t".. mvnArtifact.artifactId
- .."\t".. mvnArtifact.version
+ .."\t".. (isVersionOk and version or "")
+end
+
+
+function mod.getMvnArtifactByKey( app, key )
+ local gid, aid, version = key:match("^([^\t]+)\t([^\t]+)\t([^\t]+).*$")
+ local a = mod.newMvnArtifact()
+ a.artifactId = assert(aid, key)
+ a.groupId = assert(gid, key)
+ a.version = version
+ return a
end
function mod.onGetPomRspHdr( msg, req )
- log:write("< "..tostring(msg.proto) .." "..tostring(msg.status).." "..tostring(msg.phrase).."\n")
- --for i, h in ipairs(msg.headers) do
- -- log:write("< ".. h.key ..": ".. h.val .."\n")
- --end
- --log:write("< \n")
if msg.status ~= 200 then
+ log:write("< "..tostring(msg.proto) .." "..tostring(msg.status).." "..tostring(msg.phrase).."\n")
+ for i, h in ipairs(msg.headers) do
+ log:write("< ".. h.key ..": ".. h.val .."\n")
+ end
+ log:write("< \n")
error("Unexpected HTTP ".. tostring(msg.status))
end
assert(not req.pomParser)
req.pomParser = objectSeal{
- req = req,
- base = false,
- xmlElemStack = {},
- currentValue = false,
- mvnArtifact = objectSeal{
- parentGroupId = false,
- parentArtifactId = false,
- parentVersion = false,
- groupId = false,
- artifactId = false,
- version = false,
- },
- mvnDependency = false, -- the one we're currently parsing
- mvnMngdDependency = false, -- the one we're currently parsing
write = function( t, buf ) t.base:write(buf) end,
closeSnk = function( t, buf ) t.base:closeSnk() end,
}
@@ -200,7 +332,7 @@ function mod.onGetPomRspHdr( msg, req )
onElementEnd = function( tag, pomParser )
mod.processXmlValue(pomParser)
local elem = table.remove(pomParser.xmlElemStack)
- assert(elem.tag == tag);
+ assert(elem.tag == tag)
end,
onChunk = function( buf, pomParser )
if pomParser.currentValue then
@@ -218,23 +350,27 @@ function mod.onGetPomRspHdr( msg, req )
if not mvnArtifact.groupId then mvnArtifact.groupId = mvnArtifact.parentGroupId end
if not mvnArtifact.version then mvnArtifact.version = mvnArtifact.parentVersion end
local key = mod.getMvnArtifactKey(mvnArtifact)
- assert(not app.mvnArtifacts[key])
- app.mvnArtifacts[key] = mvnArtifact
+ if app.mvnArtifacts[key] then
+ local old = app.mvnArtifacts[key]
+ local oId = mod.getMvnArtifactKey(old)
+ local nId = mod.getMvnArtifactKey(mvnArtifact)
+ if oId ~= nId then
+ print("Already exists BUT DIFFERS:")
+ for k,v in pairs(old) do print("O",k,v) end
+ print()
+ for k,v in pairs(mvnArtifact) do print("N",k,v) end
+ error("TODO_20221215150040")
+ else
+ log:write("Already known. ReUse "..tostring(oId).."\n")
+ end
+ else
+ app.mvnArtifacts[key] = mvnArtifact
+ end
end,
}
end
-function mod.onGetPomRspChunk( buf, req )
- req.pomParser:write(buf)
-end
-
-
-function mod.onGetPomRspEnd( req )
- req.pomParser:closeSnk()
-end
-
-
function mod.resolveDependencyVersionsFromDepsMgmnt( app )
local mvnArtifacts = app.mvnArtifacts
local mvnDepsByArtifact = app.mvnDepsByArtifact
@@ -248,7 +384,7 @@ function mod.resolveDependencyVersionsFromDepsMgmnt( app )
if mvnDependency.groupId == mngdDep.groupId
and mvnDependency.artifactId == mngdDep.artifactId
then
- mvnDependency.version = assert(mngdDep.version);
+ mvnDependency.version = assert(mngdDep.version)
break
end
end
@@ -289,9 +425,8 @@ function mod.resolveProperties( app )
return str:match("^%$%{([^}]+)%}$")
end
for _, mvnArtifact in pairs(mvnArtifacts) do
- local set = nil
local depsToEnrich = {}
- set = mvnDepsByArtifact[mvnArtifact]
+ local set = mvnDepsByArtifact[mvnArtifact]
if set then for _, d in pairs(set) do
table.insert(depsToEnrich, d) end end
set = mvnMngdDepsByArtifact[mvnArtifact]
@@ -300,7 +435,18 @@ function mod.resolveProperties( app )
for _, mvnDependency in pairs(depsToEnrich) do
local propKey = getPropKey(mvnDependency.version)
if propKey then
- local propVal = mod.getPropValThroughParentChain(app, mvnArtifact, propKey)
+ local propVal
+ while true do
+ propVal = mod.getPropValThroughParentChain(app, mvnArtifact, propKey)
+ if not propVal or not propVal:find("${",0,true) then break end
+ -- there's a property-in-property. Hangle one further.
+ propKey = getPropKey(propVal)
+ if not propKey then
+ if app.warnIsError then error("Property resolver struggles with "..tostring(propVal)) end
+ log:write("[WARN ] Cannot resolve property '".. tostring(propVal) .."'\n") -- TODO
+ break
+ end
+ end
if propVal then
mvnDependency.version = propVal
end
@@ -326,6 +472,9 @@ function mod.getPropValThroughParentChain( app, mvnArtifact, propKey, none )
if propKey == "project.version" then
return mvnArtifact.version
end
+ if propKey == "project.groupId" then
+ return mvnArtifact.groupId
+ end
-- no luck in current artifact. Delegate to parent (if any)
if mvnArtifact.parentGroupId
and mvnArtifact.parentArtifactId
@@ -378,120 +527,226 @@ function mod.printStuffAtEnd( app )
end
+function mod.loadFromSqliteFile( app )
+ local db = mod.dbGetInstance(app)
+ local queryStr = "SELECT id, str FROM String"
+ local stmt = app.preparedStmts[queryStr]
+ if not stmt then stmt = db:prepare(queryStr) app.preparedStmts[queryStr] = stmt end
+ local strings = app.stringIdByStr
+ -- Load stings
+ local rs = stmt:execute()
+ while rs:next() do
+ local stringKey, stringVal
+ for iCol=1, rs:numCols() do
+ local colName = rs:name(iCol)
+ if colName == "id" then
+ assert(rs:type(iCol) == "INTEGER")
+ stringKey = rs:value(iCol)
+ elseif colName == "str" then
+ assert(rs:type(iCol) == "TEXT")
+ stringVal = rs:value(iCol)
+ else
+ error("Unexpected col String."..tostring(rs:name(iCol)))
+ end
+ end
+ assert(stringKey)
+ assert(stringVal)
+ app.stringIdByStr[stringKey] = stringVal
+ end
+ -- Load Artifacts
+ local stmtMvnArtifacts = db:prepare(""
+ .." SELECT id, groupId, artifactId, version, parentGroupId, parentArtifactId, parentVersion"
+ .." FROM MvnArtifact")
+ local mvnArtifactsByDbId = {}
+ local rs = stmtMvnArtifacts:execute()
+ assert(not app.mvnArtifacts)
+ app.mvnArtifacts = {}
+ while rs:next() do
+ local mvnArtif = mod.newMvnArtifact()
+ for iCol=1, rs:numCols() do
+ local colName = rs:name(iCol)
+ if colName == "id" then
+ mvnArtif.dbId = rs:value(iCol)
+ else
+ mvnArtif[colName] = (strings[rs:value(iCol)] or false)
+ end
+ end
+ app.mvnArtifacts[mod.getMvnArtifactKey(mvnArtif)] = mvnArtif;
+ assert(type(mvnArtif.dbId) == "number", mvnArtif.dbId)
+ mvnArtifactsByDbId[mvnArtif.dbId] = mvnArtif
+ end
+ -- Load Dependencies
+ local stmtMvnDeps = db:prepare(""
+ .." SELECT id, mvnArtifactId, needsMvnArtifactId"
+ .." FROM MvnDependency")
+ local rs = stmtMvnDeps:execute()
+ while rs:next() do
+ local mvnDep = mod.newMvnDependency()
+ local mvnArtifId, mvnDepId
+ for iCol=1, rs:numCols() do
+ local colName = rs:name(iCol)
+ if colName == "id" then
+ mvnDep.dbId = assert(rs:value(iCol))
+ elseif colName == "mvnArtifactId" then
+ mvnArtifId = assert(rs:value(iCol))
+ elseif colName == "needsMvnArtifactId" then
+ mvnDepId = assert(rs:value(iCol))
+ else
+ error("TODO_20221215134407 ".. colName)
+ end
+ end
+ local artif = mvnArtifactsByDbId[mvnArtifId]
+ local dep = mvnArtifactsByDbId[mvnDepId]
+ local deps = app.mvnDepsByArtifact[artif]
+ if not deps then deps = {} app.mvnDepsByArtifact[artif] = deps end
+ table.insert(deps, dep)
+ end
+ -- Load properties
+ local queryStr = "SELECT mvnArtifactId, keyStringId, valStringId FROM MvnProperty"
+ local stmt = app.preparedStmts[queryStr]
+ if not stmt then stmt = db:prepare(queryStr); app.preparedStmts[queryStr] = stmt end
+ local rs = stmt:execute()
+ while rs:next() do
+ local prop = { key = false, val = false, }
+ local mvnArtifactId = false
+ for iCol=1, rs:numCols() do
+ local colName = rs:name(iCol)
+ if false then
+ elseif colName == "mvnArtifactId" then
+ mvnArtifactId = rs:value(iCol)
+ elseif colName == "keyStringId" then
+ prop.key = assert(strings[rs:value(iCol)])
+ elseif colName == "valStringId" then
+ prop.val = assert(strings[rs:value(iCol)])
+ else
+ error("TODO_20230127134303 ".. tostring(colName))
+ end
+ end
+ local mvnArtifact = assert(mvnArtifactsByDbId[mvnArtifactId])
+ app.mvnPropsByArtifact[mvnArtifact] = assert(prop)
+ end
+end
+
+
+function mod.dbInsertMvnArtifact( app, mvnArtifact )
+ if mvnArtifact.dbId then warn("MvnArtifact already has dbId="..tostring(mvnArtifact.dbId)) end
+ local db = mod.dbGetInstance(app)
+ local queryStr = "INSERT INTO MvnArtifact"
+ .." groupId, artifactId, version, parentGroupId, parentArtifactId, parentVersion"
+ .." VALUES"
+ .." :groupId, :artifactId, :version, :parentGroupId, :parentArtifactId, :parentVersion"
+ .." "
+ local stmt = app.preparedStmts[queryStr]
+ if not stmt then
+ stmt = db:prepare(queryStr)
+ app.preparedStmts[queryStr] = stmt
+ end
+ stmt:reset()
+ mod.bindMvnArtifactAll(app, stmt, mvnArtifact)
+ stmt:execute()
+ if db:lastInsertRowid() ~= 0 then
+ return db:lastInsertRowid()
+ end
+ local queryStr = "SELECT id FROM MvnArtifact"
+ .." WHERE artifactId = :artifactId"
+ .." AND groupId = :groupId"
+ .." AND version = :version"
+ .." AND parentGroupId = :parentGroupId"
+ .." AND parentArtifactId = :parentArtifactId"
+ .." AND parentVersion = :parentVersion"
+ local stmt = app.preparedStmts[queryStr]
+ stmt:reset()
+ mod.bindMvnArtifactAll(app, stmt, mvnArtifact)
+ local rs = stmt:execute()
+ if not rs:next() then error("TODO_20221215172430") end
+ mvnArtifact.dbId = assert(rs:value(1))
+ if rs:next() then error("TODO_20221215172435") end
+end
+
+
+function mod.dbBindMvnArtifactAll( app, stmt, mvnArtifact )
+ stmt:bind(":groupId", mvnArtifact.groupId)
+ stmt:bind(":artifactId", mvnArtifact.artifactId)
+ stmt:bind(":version", mvnArtifact.version)
+ stmt:bind(":parentGroupId", mvnArtifact.parentGroupId)
+ stmt:bind(":parentArtifactId", mvnArtifact.parentArtifactId)
+ stmt:bind(":parentVersion", mvnArtifact.parentVersion)
+end
+
+
function mod.storeAsSqliteFile( app )
- -- TODO could we cache our prepared queries?
- local db, stmt
- if not app.sqliteOutFile then
- log:write("[INFO ] No sqliteOutFile provided. Skip export.\n")
- return
- end
- -- Query to list Artifacts and their parents:
- -- SELECT GroupId.str AS 'GID', ArtifactId.str AS 'AID', Version.str AS 'Version', ParentGid.str AS 'ParentGid', ParentAid.str AS 'ParentAid', ParentVersion.str AS 'ParentVersion'
- -- FROM MvnArtifact AS A
- -- JOIN String GroupId ON GroupId.id = A.groupId
- -- JOIN String ArtifactId ON ArtifactId.id = A.artifactId
- -- JOIN String Version ON Version.id = A.version
- -- JOIN String ParentGid ON ParentGid.id = A.parentGroupId
- -- JOIN String ParentAid ON ParentAid.id = A.parentArtifactId
- -- JOIN String ParentVersion ON ParentVersion.id = A.parentVersion
- --
- -- Query to list dependencies:
- -- SELECT GroupId.str AS 'GID', ArtifactId.str AS 'AID', Version.str AS 'Version', DepGid.str AS 'Dependency GID', DepAid.str AS 'Dependnecy AID', DepVersion.str AS 'Dependency Version'
- -- FROM MvnArtifact AS A
- -- JOIN MvnDependency AS Dep ON Dep.mvnArtifactId = A.id
- -- JOIN MvnArtifact AS D ON Dep.needsMvnArtifactId = D.id
- -- JOIN String GroupId ON GroupId.id = A.groupId
- -- JOIN String ArtifactId ON ArtifactId.id = A.artifactId
- -- JOIN String Version ON Version.id = A.version
- -- JOIN String DepGid ON DepGid.id = D.groupId
- -- JOIN String DepAid ON DepAid.id = D.artifactId
- -- JOIN String DepVersion ON DepVersion.id = D.version
- --
- db = newSqlite{
- database = app.sqliteOutFile,
- }
- db:enhancePerf()
- db:prepare("CREATE TABLE String ("
- .." id INTEGER PRIMARY KEY,"
- .." str TEXT UNIQUE)"
- ):execute()
- db:prepare("CREATE TABLE MvnArtifact ("
- .." id INTEGER PRIMARY KEY,"
- .." groupId INT,"
- .." artifactId INT,"
- .." version INT,"
- .." parentGroupId INT,"
- .." parentArtifactId INT,"
- .." parentVersion INT)"
- ):execute()
- db:prepare("CREATE TABLE MvnDependency ("
- .." id INTEGER PRIMARY KEY,"
- .." mvnArtifactId INT,"
- .." needsMvnArtifactId INT)"
- ):execute()
- --db:prepare("CREATE TABLE MvnProperty ("
- -- .." id INTEGER PRIMARY KEY,"
- -- .." keyStringId INT,"
- -- .." valStringId INT)"
- --):execute()
+ local stmt
+ local db = mod.dbGetInstance(app)
local mvnArtifactIds = {}
local mvnArtifactIdsByArtif = {}
local strings = {}
- local getStringId = function( str ) -- create/reUse strings on-demand
- if not str then return nil end
- local stringId = strings[str]
- if not stringId then
- local stmt = db:prepare("INSERT INTO String (str)VALUES(:str)")
- stmt:reset()
- stmt:bind(":str", str)
- stmt:execute()
- stringId = db:lastInsertRowid()
- strings[str] = stringId
- end
- return stringId
- end
- local stmtInsMvnArtifact = db:prepare("INSERT INTO MvnArtifact"
- .."('groupId', 'artifactId', 'version', 'parentGroupId', 'parentArtifactId', 'parentVersion')"
- .."VALUES"
- .."(:groupId , :artifactId , :version , :parentGroupId , :parentArtifactId , :parentVersion )")
+ mod.dbInitTables(app)
+ local queryStr = "INSERT INTO MvnArtifact"
+ .." ('groupId', 'artifactId', 'version', 'parentGroupId', 'parentArtifactId', 'parentVersion')"
+ .." VALUES"
+ .." (:groupId , :artifactId , :version , :parentGroupId , :parentArtifactId , :parentVersion )"
+ .." ON CONFLICT DO NOTHING"
+ local stmt = app.preparedStmts[queryStr]
+ if not stmt then stmt = db:prepare(queryStr) app.preparedStmts[queryStr] = stmt end
local insertMvnArtifact = function(a)
- assert(a.groupId and a.artifactId and a.version)
+ if a.dbId then
+ -- TODO analyze this case.
+ --log:write("[WARN ] MvnArtifact "..tostring(a.dbId).." probably already exists. Insert it again\n")
+ end
+ assert(a.groupId and a.artifactId)
+ if not a.version then warn("version missing of "..a.groupId.." "..a.artifactId) end
if a.parentGroupId then assert(a.parentArtifactId and a.parentVersion)
else assert(not a.parentArtifactId and not a.parentVersion) end
- stmtInsMvnArtifact:reset()
- stmtInsMvnArtifact:bind(":groupId", getStringId(a.groupId))
- stmtInsMvnArtifact:bind(":artifactId", getStringId(a.artifactId))
- stmtInsMvnArtifact:bind(":version", getStringId(a.version))
- stmtInsMvnArtifact:bind(":parentGroupId", getStringId(a.parentGroupId))
- stmtInsMvnArtifact:bind(":parentArtifactId", getStringId(a.parentArtifactId))
- stmtInsMvnArtifact:bind(":parentVersion", getStringId(a.parentVersion))
- stmtInsMvnArtifact:execute()
+ local versionDbId = (a.version and mod.dbGetOrNewString(app, a.version) or nil)
+ stmt:reset()
+ stmt:bind(":groupId", mod.dbGetOrNewString(app, a.groupId))
+ stmt:bind(":artifactId", mod.dbGetOrNewString(app, a.artifactId))
+ stmt:bind(":version", versionDbId)
+ stmt:bind(":parentGroupId", mod.dbGetOrNewString(app, a.parentGroupId))
+ stmt:bind(":parentArtifactId", mod.dbGetOrNewString(app, a.parentArtifactId))
+ stmt:bind(":parentVersion", mod.dbGetOrNewString(app, a.parentVersion))
+ stmt:execute()
local dbId = db:lastInsertRowid()
+ if dbId == 0 then
+ -- Seems as entry already exists. So need to query its id separately.
+ local stmt = db:prepare("SELECT id FROM MvnArtifact"
+ .." WHERE groupId = :groupId AND artifactId = :artifactId AND version = :version"
+ .." AND parentGroupId = :parentGroupId AND parentArtifactId = :parentArtifactId AND parentVersion = :parentVersion")
+ stmt:reset()
+ stmt:bind(":groupId", mod.dbGetOrNewString(app, a.groupId))
+ stmt:bind(":artifactId", mod.dbGetOrNewString(app, a.artifactId))
+ stmt:bind(":version", versionDbId)
+ stmt:bind(":parentGroupId", mod.dbGetOrNewString(app, a.parentGroupId))
+ stmt:bind(":parentArtifactId", mod.dbGetOrNewString(app, a.parentArtifactId))
+ stmt:bind(":parentVersion", mod.dbGetOrNewString(app, a.parentVersion))
+ local rs = stmt:execute()
+ dbId = rs:value(1)
+ assert(dbId)
+ end
mvnArtifactIds[a] = dbId -- TODO MUST be byString
local bucket = mvnArtifactIdsByArtif[assert(a.artifactId)]
if not bucket then bucket = {} mvnArtifactIdsByArtif[a.artifactId] = bucket end
table.insert(bucket, { dbId = dbId, mvnArtifact = a, })
return dbId
end
- -- Store artifacts
+ -- Store new artifacts
for _, mvnArtifact in pairs(app.mvnArtifacts) do
insertMvnArtifact(mvnArtifact)
- local mvnDeps = app.mvnDepsByArtifact[mvnArtifact] or {}
- -- dependencies are nothing else than artifacts
- for _, mvnDep in pairs(mvnDeps) do
+ if mvnArtifact.parentArtifactId then
+ -- TODO? maybe?
end
end
-- Store dependencies
- local stmt = db:prepare("INSERT INTO MvnDependency"
- .."('mvnArtifactId', 'needsMvnArtifactId')"
- .."VALUES"
- .."(:mvnArtifactId , :needsMvnArtifactId )")
+ local queryStr = "INSERT INTO MvnDependency"
+ .." ( mvnArtifactId, needsMvnArtifactId)"
+ .." VALUES"
+ .." ( :mvnArtifactId, :needsMvnArtifactId)"
+ local stmt = app.preparedStmts[queryStr]
+ if not stmt then stmt = db:prepare(queryStr) app.preparedStmts[queryStr] = stmt end
for _, mvnArtifact in pairs(app.mvnArtifacts) do
local mvnDeps = app.mvnDepsByArtifact[mvnArtifact]
for _, mvnDep in pairs(mvnDeps or {}) do
- if not mvnDep.version then mvnDep.version = "TODO_5bbc0e87011e24d845136c5406302616" end
- assert(mvnDep.version, mvnDep.artifactId)
- assert(mvnDep.groupId and mvnDep.artifactId and mvnDep.version)
+ assert(mvnDep.groupId and mvnDep.artifactId)
local bucket = mvnArtifactIdsByArtif[mvnDep.artifactId]
local depId = nil
for _,a in pairs(bucket or {}) do
@@ -503,9 +758,11 @@ function mod.storeAsSqliteFile( app )
end
if not depId then -- Artifact not stored yet. Do now.
depId = insertMvnArtifact({
- groupId = mvnDep.groupId,
- artifactId = mvnDep.artifactId,
- version = mvnDep.version,
+ groupId = assert(mvnDep.groupId),
+ artifactId = assert(mvnDep.artifactId),
+ -- mvnDep.version MAY be missing. Eg via depMgnt of
+ -- unknown parent or similar
+ version = (mvnDep.version),
})
end
stmt:reset()
@@ -514,119 +771,559 @@ function mod.storeAsSqliteFile( app )
stmt:execute()
end
end
- db:close()
+ -- Store properties
+ local queryStr = "INSERT INTO MvnProperty"
+ .." ( mvnArtifactId, keyStringId, valStringId )"
+ .." VALUES"
+ .." ( :mvnArtifactId, :keyStringId, :valStringId )"
+ local stmt = app.preparedStmts[queryStr]
+ if not stmt then stmt = db:prepare(queryStr); app.preparedStmts[queryStr] = stmt end
+ for _, mvnArtifact in pairs(app.mvnArtifacts) do
+ assert(type(mvnArtifact) == "table")
+ local dbId = mvnArtifactIds[mvnArtifact]
+ local mvnProps = app.mvnPropsByArtifact[mvnArtifact]
+ if not mvnProps then goto nextArtifact end
+ assert(type(mvnProps) == "table")
+ for _, mvnProp in ipairs(mvnProps) do
+ stmt:reset()
+ stmt:bind(":mvnArtifactId", assert(dbId))
+ stmt:bind(":keyStringId", mod.dbGetOrNewString(app, assert(mvnProp.key)))
+ stmt:bind(":valStringId", mod.dbGetOrNewString(app, assert(mvnProp.val)))
+ stmt:execute()
+ --local dbId = db:lastInsertRowid()
+ end
+ ::nextArtifact::
+ end
end
-function mod.run( app )
- assert(not app.mvnArtifacts) app.mvnArtifacts = {}
- assert(not app.mvnPropsByArtifact) app.mvnPropsByArtifact = {}
- assert(not app.mvnDepsByArtifact) app.mvnDepsByArtifact = {}
- assert(not app.mvnMngdDepsByArtifact) app.mvnMngdDepsByArtifact = {}
- local pomSrc = mod.newPomUrlSrc(app)
- while true do
- local pomUrl = pomSrc:nextPomUrl()
- if not pomUrl then break end
- local proto = pomUrl:match("^(https?)://")
- local isTLS = (proto:upper() == "HTTPS")
- local host = pomUrl:match("^https?://([^:/]+)[:/]")
- local port = pomUrl:match("^https?://[^:/]+:(%d+)[^%d]")
- local url = pomUrl:match("^https?://[^/]+(.*)$")
- if port == 443 then isTLS = true end
- if not port then port = (isTLS and 443 or 80) end
- log:write("> GET ".. proto .."://".. host ..":".. port .. url .."\n")
- local req = objectSeal{
- app = app,
- base = false,
- pomParser = false,
- }
- req.base = app.http:request{
- cls = req,
- host = assert(host), port = assert(port),
- method = "GET", url = url,
- --hdrs = ,
- useTLS = isTLS,
- onRspHdr = mod.onGetPomRspHdr,
- onRspChunk = mod.onGetPomRspChunk,
- onRspEnd = mod.onGetPomRspEnd,
- }
- req.base:closeSnk()
+-- returns dbId of the (new or existing) string
+function mod.dbGetOrNewString( app, str )
+ local db = mod.dbGetInstance(app)
+ local tryCnt = 0
+ --log:write("[DEBUG] Searching String ID for '"..tostring(str).."'\n")
+ if not str then return nil end
+::startOver::
+ -- Ask inMemory cache
+ local stringId = app.stringIdByStr[str]
+ if stringId then
+ --log:write("[DEBUG] Using String ".. stringId .." for '"..str.."'\n")
+ return stringId
end
- log:write("[INFO ] No more pom URLs\n")
- mod.resolveDependencyVersionsFromDepsMgmnt(app)
- mod.resolveProperties(app)
- mod.storeAsSqliteFile(app)
- --mod.printStuffAtEnd(app)
+ -- Ask DB
+ local queryStr = "SELECT id FROM String WHERE str = :str"
+ local stmt = app.preparedStmts[queryStr]
+ if not stmt then stmt = db:prepare(queryStr) app.preparedStmts[queryStr] = stmt end
+ stmt:reset()
+ stmt:bind(":str", str)
+ local rs = stmt:execute()
+ if rs:next() then -- DB has an entry :)
+ stringId = assert(rs:value(1))
+ if rs:next() then log:write("[WARN ] DB string duplication: '"..tostring(str).."'\n") end
+ --log:write("[DEBUG] Using OLD String ".. stringId .."\n")
+ app.stringIdByStr[str] = stringId
+ return stringId
+ end
+ stmt:close() app.preparedStmts[queryStr] = nil -- TODO WTF?!?
+ --log:write("[DEBUG] None in DB yet. Make sure it exists\n")
+ local queryStr = "INSERT INTO String (str)VALUES(:str) ON CONFLICT DO NOTHING"
+ local stmt = app.preparedStmts[queryStr]
+ if not stmt then stmt = db:prepare(queryStr) app.preparedStmts[queryStr] = stmt end
+ stmt:reset()
+ stmt:bind(":str", str)
+ stmt:execute()
+ stmt:close() app.preparedStmts[queryStr] = nil -- TODO WTF?!?
+ --log:write("[DEBUG] Then try again\n")
+ if tryCnt > 3 then error("TODO_20221215185428 fixme") end
+ tryCnt = tryCnt +1
+ goto startOver -- recursion stinks
end
+function mod.dbInitTables( app )
+ local db = mod.dbGetInstance(app)
+ db:prepare("CREATE TABLE IF NOT EXISTS String ("
+ .." id INTEGER PRIMARY KEY,"
+ .." str TEXT UNIQUE)"
+ ):execute()
+ db:prepare("CREATE TABLE IF NOT EXISTS MvnArtifact ("
+ .." id INTEGER PRIMARY KEY,"
+ .." groupId INT,"
+ .." artifactId INT,"
+ .." version INT,"
+ .." parentGroupId INT,"
+ .." parentArtifactId INT,"
+ .." parentVersion INT)"
+ ):execute()
+ db:prepare("CREATE TABLE IF NOT EXISTS MvnDependency ("
+ .." id INTEGER PRIMARY KEY,"
+ .." mvnArtifactId INT,"
+ .." needsMvnArtifactId INT)"
+ ):execute()
+ db:prepare("CREATE TABLE IF NOT EXISTS MvnProperty ("
+ .." id INTEGER PRIMARY KEY,"
+ .." mvnArtifactId INTEGER,"
+ .." keyStringId INTEGER,"
+ .." valStringId INTEGER)"
+ ):execute()
+end
+
+
+function mod.dbGetInstance( app )
+ local db = app.sqlite
+ if not db then
+ db = newSqlite{ database = app.statePath, }
+ db:enhancePerf()
+ app.sqlite = db
+ end
+ return db
+end
+
+
+-- Using custom impl because builtin cannot do connection pooling yet
function mod.newSocketMgr()
- local hosts = {}
- local openSock = function( t, opts )
+ local AF_INET = require('scriptlee').posix.AF_INET
+ local AF_INET6 = require('scriptlee').posix.AF_INET6
+ local IPPROTO_TCP = require('scriptlee').posix.IPPROTO_TCP
+ local SOCK_STREAM = require('scriptlee').posix.SOCK_STREAM
+ local inaddrOfHostname = require('scriptlee').posix.inaddrOfHostname
+ local newTlsClient = require('scriptlee').newTlsClient
+ local socket = require('scriptlee').posix.socket
+ local S = {}
+ local idleSocketsBySockaddr = {}
+
+ local openSock = function(t, opts)
for k, v in pairs(opts) do
if false then
- elseif k=='host' or k=='port' then
- elseif k=='useTLS' then
- if v then error('TLS not impl') end
+ elseif k=='host' or k=='port' or k=='useTLS' then
+ -- ok
else
error('Unknown option: '..tostring(k))
end
end
+
local inaddr = inaddrOfHostname(opts.host)
local af
if inaddr:find('^%d+.%d+.%d+.%d+$') then af = AF_INET else af = AF_INET6 end
- local sock = socket(af, SOCK_STREAM, IPPROTO_TCP)
- sock:connect(inaddr, opts.port)
- log:write("opts.useTLS "..tostring(opts.useTLS).." (Override to TRUE ...)\n")
- opts.useTLS = true -- TODO why the heck is this needed? (I guess scriptlee bug?)
- if opts.useTLS then
- local sockUnderTls = sock
- sock = newTlsClient{
- cls = assert(sockUnderTls),
- peerHostname = assert(opts.host),
- onVerify = function( tlsIssues, sockUnderTls )
- if tlsIssues.CERT_NOT_TRUSTED then
- warn("TLS ignore CERT_NOT_TRUSTED");
- tlsIssues.CERT_NOT_TRUSTED = false
- end
- end,
- send = function( buf, sockUnderTls )
- local ret = sockUnderTls:write(buf)
- sockUnderTls:flush() -- TODO Why is this flush needed?
- return ret
- end,
- recv = function( sockUnderTls ) return sockUnderTls:read() end,
- flush = function( sockUnderTls ) sockUnderTls:flush() end,
- closeSnk = function( sockUnderTls ) sockUnderTls:closeSnk() end,
- }
- assert(not getmetatable(sock).release)
- getmetatable(sock).release = function( t ) sockUnderTls:release() end;
+ local sockaddr = inaddr ..":".. (opts.port or "-1")
+ local poolForThisHost = idleSocketsBySockaddr[sockaddr]
+ local sock = poolForThisHost and table.remove(poolForThisHost) or false
+ if not sock then
+ -- no sock from pool. Create new one.
+ sock = socket(af, SOCK_STREAM, IPPROTO_TCP)
+ sock:connect(inaddr, opts.port)
+ if opts.useTLS then
+ local sockUnderTls = sock
+ sock = newTlsClient{
+ cls = sockUnderTls,
+ peerHostname = opts.host,
+ onVerify = function( tlsIssues, sockUnderTls )
+ if tlsIssues.CERT_NOT_TRUSTED then
+ warn('TLS ignore CERT_NOT_TRUSTED');
+ tlsIssues.CERT_NOT_TRUSTED = false
+ end
+ end,
+ send = function( buf, sockUnderTls )
+ local ret = sockUnderTls:write(buf)
+ sockUnderTls:flush()
+ return ret
+ end,
+ recv = function( sockUnderTls ) return sockUnderTls:read() end,
+ flush = function( sockUnderTls ) sockUnderTls:flush() end,
+ closeSnk = function( sockUnderTls ) sockUnderTls:closeSnk() end,
+ }
+ assert(not getmetatable(sock).release)
+ getmetatable(sock).release = function( t ) sockUnderTls:release() end;
+ end
end
- return {
- _sock = sock,
- write = function(t, ...) return sock:write(...)end,
- read = function(t, ...) return sock:read(...)end,
- flush = function(t, ...) return sock:flush(...)end,
+ return{
+ [S] = sock,
+ _sockaddr = sockaddr,
+ write = function(t, ...)return sock:write(...)end,
+ read = function(t, ...)return sock:read(...)end,
+ flush = function(t, ...)return sock:flush(...)end,
}
end
+
+ local releaseSock = function( t, mySock )
+ -- TODO just ignroe cleanup for now because we have no connection pooling yet.
+ local poolForThisHost = idleSocketsBySockaddr[mySock._sockaddr]
+ if not poolForThisHost then
+ poolForThisHost = {}
+ idleSocketsBySockaddr[mySock._sockaddr] = poolForThisHost
+ end
+ table.insert(poolForThisHost, assert(mySock[S]))
+ end
+
+ local closeSock = function( t, mySock )
+ mySock[S]:release()
+ end
+
return{
openSock = openSock,
- releaseSock = function(t, sockWrapr) t:closeSock(sockWrapr) end,
- closeSock = function(t, sockWrapr) sockWrapr._sock:release() end,
+ releaseSock = releaseSock,
+ closeSock = closeSock,
}
end
+function mod.printCsvParents( app )
+ local db = mod.dbGetInstance(app)
+ local queryStr = "" -- Query
+ .." SELECT DISTINCT"
+ .." GroupId.str,"
+ .." ArtifactId.str,"
+ .." Version.str,"
+ .." ParentGid.str,"
+ .." ParentAid.str,"
+ .." ParentVersion.str"
+ .." FROM MvnArtifact AS A"
+ .." JOIN String GroupId ON GroupId.id = A.groupId"
+ .." JOIN String ArtifactId ON ArtifactId.id = A.artifactId"
+ .." JOIN String Version ON Version.id = A.version"
+ .." LEFT JOIN String ParentGid ON ParentGid.id = A.parentGroupId"
+ .." LEFT JOIN String ParentAid ON ParentAid.id = A.parentArtifactId"
+ .." LEFT JOIN String ParentVersion ON ParentVersion.id = A.parentVersion"
+ local stmt = app.preparedStmts[queryStr]
+ if not stmt then stmt = db:prepare(queryStr) app.preparedStmts[queryStr] = stmt end
+ stmt:reset()
+ local rs = stmt:execute()
+ out:write("h;Created;"..mod.escapeCsvValue(os.date("%Y-%m-%d %H:%m:%S")).."\n")
+ out:write("c;GID;AID;Version;ParentGID;ParentAID;ParentVersion\n")
+ local nilVal = app.nullvalue
+ while rs:next() do
+ out:write("r;") out:write(mod.escapeCsvValue(rs:value(1) or nilVal))
+ out:write(";") out:write(mod.escapeCsvValue(rs:value(2) or nilVal))
+ out:write(";") out:write(mod.escapeCsvValue(rs:value(3) or nilVal))
+ out:write(";") out:write(mod.escapeCsvValue(rs:value(4) or nilVal))
+ out:write(";") out:write(mod.escapeCsvValue(rs:value(5) or nilVal))
+ out:write(";") out:write(mod.escapeCsvValue(rs:value(6) or nilVal))
+ out:write("\n")
+ end
+ out:write("t;status;OK\n")
+end
+
+
+function mod.printCsvDependencies( app )
+ local db = mod.dbGetInstance(app)
+ local queryStr = "" -- Query
+ .." SELECT DISTINCT"
+ .." GroupId.str,"
+ .." ArtifactId.str,"
+ .." Version.str,"
+ .." DepGid.str,"
+ .." DepAid.str,"
+ .." DepVersion.str"
+ .." FROM MvnArtifact AS A"
+ .." JOIN MvnDependency AS Dep ON Dep.mvnArtifactId = A.id"
+ .." LEFT JOIN MvnArtifact AS D ON Dep.needsMvnArtifactId = D.id"
+ .." LEFT JOIN String GroupId ON GroupId.id = A.groupId"
+ .." LEFT JOIN String ArtifactId ON ArtifactId.id = A.artifactId"
+ .." LEFT JOIN String Version ON Version.id = A.version"
+ .." LEFT JOIN String DepGid ON DepGid.id = D.groupId"
+ .." LEFT JOIN String DepAid ON DepAid.id = D.artifactId"
+ .." LEFT JOIN String DepVersion ON DepVersion.id = D.version"
+ local stmt = app.preparedStmts[queryStr]
+ if not stmt then stmt = db:prepare(queryStr) app.preparedStmts[queryStr] = stmt end
+ stmt:reset()
+ local rs = stmt:execute()
+ out:write("h;Created;"..mod.escapeCsvValue(os.date("%Y-%m-%d %H:%m:%S")).."\n")
+ out:write("c;GID;AID;Version;DepGID;DepAID;DepVersion\n")
+ local nilVal = app.nullvalue
+ while rs:next() do
+ out:write("r;") out:write(mod.escapeCsvValue(rs:value(1) or nilVal))
+ out:write(";") out:write(mod.escapeCsvValue(rs:value(2) or nilVal))
+ out:write(";") out:write(mod.escapeCsvValue(rs:value(3) or nilVal))
+ out:write(";") out:write(mod.escapeCsvValue(rs:value(4) or nilVal))
+ out:write(";") out:write(mod.escapeCsvValue(rs:value(5) or nilVal))
+ out:write(";") out:write(mod.escapeCsvValue(rs:value(6) or nilVal))
+ out:write("\n")
+ end
+ out:write("t;status;OK\n")
+end
+
+
+function mod.escapeCsvValue( str )
+ local typ = type(str)
+ if typ == "string" then
+ if str:find("[;\r\n\"]") then
+ str = '"'.. str:gsub('"', '""') ..'"' end
+ else
+ error("TODO_20221215181624 "..tostring(typ))
+ end
+ return str
+end
+
+
+function mod.enrichFromCbacks( app, opts )
+ local writeNextPomTo = assert(opts.writeNextPomTo)
+ local onParentPomMissing = assert(opts.onParentPomMissing)
+ opts = nil
+ while true do -- TODO is this loop reall what we want?
+ local pomParser = false
+ local ok = writeNextPomTo(objectSeal{
+ write = function( t, buf, beg, len )
+ if not pomParser then
+ pomParser = objectSeal{
+ app = app,
+ base = false,
+ xmlElemStack = {},
+ currentValue = false,
+ mvnArtifact = mod.newMvnArtifact(),
+ mvnDependency = false, -- the one we're currently parsing
+ mvnMngdDependency = false, -- the one we're currently parsing
+ write = function( pomParser, buf, beg, len )
+ assert(beg == 1)
+ assert(buf:len() == len)
+ return pomParser.base:write(buf)
+ end,
+ closeSnk = function( pomParser )
+ return pomParser.base:closeSnk()
+ end,
+ }
+ pomParser.base = newXmlParser{
+ cls = pomParser,
+ onElementBeg = function( tag, pomParser )
+ table.insert(pomParser.xmlElemStack, { tag = tag, })
+ pomParser.currentValue = false
+ end,
+ onElementEnd = function( tag, pomParser )
+ mod.processXmlValue(pomParser)
+ local elem = table.remove(pomParser.xmlElemStack)
+ assert(elem.tag == tag);
+ end,
+ onChunk = function( buf, pomParser )
+ if pomParser.currentValue then
+ pomParser.currentValue = pomParser.currentValue .. buf
+ else
+ pomParser.currentValue = buf
+ end
+ end,
+ onEnd = function( pomParser )
+ assert(#pomParser.xmlElemStack == 0)
+ local app = pomParser.app
+ local mvnArtifact = pomParser.mvnArtifact
+ pomParser.mvnArtifact = false
+ if not mvnArtifact.groupId then
+ mvnArtifact.groupId = mvnArtifact.parentGroupId end
+ if not mvnArtifact.version then
+ mvnArtifact.version = mvnArtifact.parentVersion end
+ local key = mod.getMvnArtifactKey(mvnArtifact)
+ if app.mvnArtifacts[key] then
+ local old = app.mvnArtifacts[key]
+ local oId = mod.getMvnArtifactKey(old)
+ local nId = mod.getMvnArtifactKey(mvnArtifact)
+ if oId ~= nId then
+ print("Already exists BUT DIFFERS:")
+ for k,v in pairs(old) do print("O",k,v) end
+ print()
+ for k,v in pairs(mvnArtifact) do print("N",k,v) end
+ error("TODO_20221215150040")
+ else
+ log:write("Already known. ReUse "..tostring(oId).."\n")
+ end
+ else
+ app.mvnArtifacts[key] = mvnArtifact
+ end
+ -- Check for missing poms.
+ if mvnArtifact.parentArtifactId then
+ local key = mod.getMvnArtifactKey({
+ artifactId = mvnArtifact.parentArtifactId,
+ groupId = mvnArtifact.parentGroupId,
+ version = mvnArtifact.parentVersion,
+ })
+ if not app.mvnArtifacts[key] then -- parent pom missing
+ onParentPomMissing(
+ mvnArtifact.parentGroupId,
+ mvnArtifact.parentArtifactId,
+ mvnArtifact.parentVersion)
+ end
+ end
+ end,
+ }
+ end
+ pomParser:write(buf, beg, len)
+ end,
+ closeSnk = function()
+ if not pomParser then
+ return -- can happen on 404 because empty body (see also close in http rsp handler)
+ end
+ pomParser:closeSnk()
+ end,
+ })
+ if not ok then break end
+ end
+ log:write("[INFO ] No more pom URLs\n")
+ mod.resolveDependencyVersionsFromDepsMgmnt(app)
+ mod.resolveProperties(app)
+ mod.storeAsSqliteFile(app)
+ log:write("\n\nState DUMP:\n\n")
+ mod.printStuffAtEnd(app)
+end
+
+
+function mod.fileExists( pomFilepath )
+ local fd = io.open(pomFilepath)
+ local exists = not not fd
+ fd:close()
+ return exists
+end
+
+
+-- Deprecated. Use the callback variant
+function mod.enrichFromUrls( app )
+ local pomSrc = mod.newPomUrlSrc(app)
+ local missingPoms, missingDone = {}, {}
+ mod.enrichFromCbacks(app, objectSeal{
+ onParentPomMissing = function( gid, aid, version )
+ local a = mod.newMvnArtifact()
+ a.artifactId = aid
+ a.groupId = gid
+ a.version = version
+ local artifactKey = mod.getMvnArtifactKey(a)
+ local url = mod.urlByArtifact(app, a)
+ assert(artifactKey)
+ if not missingDone[artifactKey] then missingPoms[artifactKey] = true end
+ end,
+ writeNextPomTo = function( snk )
+ local pomArtifact = pomSrc:nextPomArtifact()
+ local pomKey = nil
+ if not pomArtifact then
+ pomKey, _ = pairs(missingPoms)(missingPoms)
+ if pomKey then
+ pomArtifact = mod.getMvnArtifactByKey(app, pomKey)
+ missingDone[pomKey] = true
+ missingPoms[pomKey] = nil
+ log:write("NeedAlso: ".. pomKey .."\n")
+ end
+ end
+ if not pomArtifact then
+ log:write("No more poms\n")
+ return false
+ end
+ local pomFilepath = mod.pomFilepathByArtifact(app, pomArtifact)
+ local fd = io.open(pomFilepath, "rb")
+ -- MUST NOT use local cache for mvn SNAPSHOTs
+ -- MUST NOT try to read server-placholder '[RELEASE]' from local cache
+ if fd and not pomFilepath:find("SNAPSHOT",0,true) and not pomFilepath:find("RELEASE",0,true) then
+ log:write("> fread(".. pomFilepath ..")\n")
+ local file = io.open(pomFilepath, "rb")
+ while true do
+ local buf = file:read(1<<14)
+ if buf then
+ snk:write(buf, 1, buf:len())
+ else
+ file:close()
+ snk:closeSnk()
+ return true
+ end
+ end
+ --else
+ -- log:write("[DEBUG] NoSuchFile ".. pomFilepath .."\n")
+ end
+ -- No local file. Go the HTTP way.
+ local pomUrl = mod.urlByArtifact(app, pomArtifact)
+ local proto = pomUrl:match("^(https?)://")
+ local isTLS = (proto:upper() == "HTTPS")
+ local host = pomUrl:match("^https?://([^:/]+)[:/]")
+ local port = pomUrl:match("^https?://[^:/]+:(%d+)[^%d]")
+ local url = pomUrl:match("^https?://[^/]+(.*)$")
+ if port == 443 then isTLS = true end
+ if not port then port = (isTLS and 443 or 80) end
+ log:write("> GET ".. proto .."://".. host ..":".. port .. url .."\n")
+ local req = objectSeal{
+ app = app,
+ base = false,
+ pomParser = false,
+ pomArtifact = pomArtifact,
+ responseIsOk = false,
+ }
+ req.base = app.http:request{
+ cls = req,
+ host = assert(host), port = assert(port),
+ method = "GET", url = url,
+ useTLS = isTLS,
+ onRspHdr = function( msg, req )
+ if msg.status ~= 200 then
+ log:write("< "..tostring(msg.proto) .." "..tostring(msg.status).." "..tostring(msg.phrase).."\n")
+ for i, h in ipairs(msg.headers) do
+ log:write("< ".. tostring(h[1]) ..": ".. tostring(h[2]) .."\n")
+ end
+ log:write("< \n")
+ local a = req.pomArtifact
+ local msg = "Unexpected HTTP ".. tostring(msg.status) .." for ".. a.groupId .." ".. a.artifactId .." ".. a.version
+ if app.warnIsError then error(msg) else log:write("[WARN ] ".. msg .."\n") end
+ else
+ req.responseIsOk = true
+ end
+ end,
+ onRspChunk = function( buf, req )
+ if req.responseIsOk then snk:write(buf, 1, buf:len()) end
+ end,
+ onRspEnd = function( req )
+ snk:closeSnk()
+ end,
+ }
+ local ok, emsg = pcall(req.base.closeSnk, req.base)
+ if not ok then
+ if tostring(emsg) == "ENOMSG" then
+ -- This is a bug in scriptlee. It should report 404.
+ log:write(tostring(emsg).."\n")
+ snk:closeSnk()
+ else
+ error(emsg)
+ end
+ end
+ return true
+ end,
+ })
+end
+
+
+function mod.run( app )
+ assert(not app.mvnPropsByArtifact) app.mvnPropsByArtifact = {}
+ assert(not app.mvnDepsByArtifact) app.mvnDepsByArtifact = {}
+ assert(not app.mvnMngdDepsByArtifact) app.mvnMngdDepsByArtifact = {}
+ local fileExists = io.open(app.statePath, "rb")
+ if fileExists then
+ io.close(fileExists)
+ mod.loadFromSqliteFile(app)
+ else
+ assert(not app.mvnArtifacts)
+ app.mvnArtifacts = {}
+ end
+ if false then
+ elseif app.asCsv == "parents" then
+ mod.printCsvParents(app)
+ elseif app.asCsv == "deps" then
+ mod.printCsvDependencies(app)
+ elseif app.csvToFetch then
+ mod.enrichFromUrls(app)
+ else
+ error("TODO_20221215175852")
+ end
+ if app.sqlite then app.sqlite:close() app.sqlite = false end
+end
+
+
function mod.main()
local app = objectSeal{
http = newHttpClient{
socketMgr = assert(mod.newSocketMgr()),
},
+ warnIsError = false,
+ csvToFetch = false,
+ uripat = false,
+ asCsv = false,
+ nullvalue = false,
mvnArtifacts = false,
mvnPropsByArtifact = false,
mvnDepsByArtifact = false,
mvnMngdDepsByArtifact = false,
- sqliteOutFile = false,
+ sqlite = false,
+ statePath = false,
+ preparedStmts = {},
+ stringIdByStr = {},
}
if mod.parseArgs(app) ~= 0 then os.exit(1) end
mod.run(app)