e: java.lang.NoSuchMethodError: 'void kotlin.script.experimental.api.KotlinType.<init>(kotlin.reflect.KClass, boolean, int, kotlin.jvm.internal.DefaultConstructorMarker)'
at org.jetbrains.kotlin.scripting.definitions.ScriptCompilationConfigurationFromDefinition$1.invoke(ScriptCompilationConfigurationFromDefinition.kt:32)
at org.jetbrains.kotlin.scripting.definitions.ScriptCompilationConfigurationFromDefinition$1.invoke(ScriptCompilationConfigurationFromDefinition.kt:28)
at kotlin.script.experimental.api.ScriptCompilationConfiguration.<init>(scriptCompilation.kt:23)
at kotlin.script.experimental.api.ScriptCompilationConfiguration.<init>(scriptCompilation.kt:25)
at org.jetbrains.kotlin.scripting.definitions.ScriptCompilationConfigurationFromDefinition.<init>(ScriptCompilationConfigurationFromDefinition.kt:27)
at org.jetbrains.kotlin.scripting.definitions.ScriptDefinition$Companion.getDefault(ScriptDefinition.kt:221)
at org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationExtension.updateConfiguration(ScriptingCompilerConfigurationExtension.kt:67)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment$Companion.configureProjectEnvironment(KotlinCoreEnvironment.kt:578)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.<init>(KotlinCoreEnvironment.kt:199)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.<init>(KotlinCoreEnvironment.kt:108)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment$Companion.createForProduction(KotlinCoreEnvironment.kt:445)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.createCoreEnvironment(K2JVMCompiler.kt:192)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:143)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:53)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:99)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:47)
at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:475)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:125)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:373)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally$default(IncrementalCompilerRunner.kt:318)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.rebuild(IncrementalCompilerRunner.kt:114)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:207)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:79)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:625)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:101)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1746)
at jdk.internal.reflect.GeneratedMethodAccessor26.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360)
at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:712)
at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:587)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:705)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:704)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
一方 kweb では、クライアントサイドで動くコードを書くから大変なんだ!クライアントサイドで動くコードを書くのが大変なら全部サーバーサイドで動かせばいいじゃない!というアプローチをとっています。
ブラウザ上でのイベント発生時にサーバー側に通信を行って、サーバー側で処理します。そして、サーバー側で DOM を操作すれば、それがクライアントサイドに伝搬する感じになっています。
これらの通信を都度都度行うと高コストなので、websocket で通信を張りっぱなしにしています。
これはなんというか、まぁ通信コスト等も高いですし、富豪的プログラミング だなぁ、という感じですね!
富豪的ではありつつも、めちゃくちゃ簡単にインタラクティブなウェブページを宣言的に書くことができて楽しいです。僕のこのブログの編集画面も kweb で書かれていますが、以前の spring webmvc で書かれていたバージョンに比べると圧倒的にいじりやすくなっていますし、SPA をめちゃくちゃ簡単に実装できるようになっています。
たとえば、簡単な web chat システムを書く場合、以下のように書くだけ。client side と server side の通信プロセスは隠蔽されています。ObservableList に入れたものは、それを watch しているページを表示しているクライアントがあれば、それらすべてに websocket で描画の更新内容が伝搬されます。
実際にどういうふうに通信されてるかは chrome の inspector などで見るのが良いです。
Thread safety error; this instance of WebDriver was constructed on thread Test worker (id 1) and is being accessed by thread awaitility-thread (id 92)This is not permitted and *will* cause undefined behaviour
Build info: version: '4.6.0', revision: '79f1c02ae20'
System info: os.name: 'Mac OS X', os.arch: 'aarch64', os.version: '12.6', java.version: '17.0.2'
Driver info: driver.version: unknown
org.openqa.selenium.WebDriverException: Thread safety error; this instance of WebDriver was constructed on thread Test worker (id 1) and is being accessed by thread awaitility-thread (id 92)This is not permitted and *will* cause undefined behaviour
Build info: version: '4.6.0', revision: '79f1c02ae20'
System info: os.name: 'Mac OS X', os.arch: 'aarch64', os.version: '12.6', java.version: '17.0.2'
Driver info: driver.version: unknown
at app//org.openqa.selenium.support.ThreadGuard$WebDriverInvocationHandler.invoke(ThreadGuard.java:88)
at app/jdk.proxy3/jdk.proxy3.$Proxy28.getCurrentUrl(Unknown Source)
at app//InputCheckedTest.checkBeforeAndAfterClick$lambda$0(MyTest.kt:55)
at app//org.awaitility.core.AssertionCondition.lambda$new$0(AssertionCondition.java:53)
at app//org.awaitility.core.ConditionAwaiter$ConditionPoller.call(ConditionAwaiter.java:248)
at app//org.awaitility.core.ConditionAwaiter$ConditionPoller.call(ConditionAwaiter.java:235)
at java.base@17.0.2/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base@17.0.2/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base@17.0.2/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base@17.0.2/java.lang.Thread.run(Thread.java:833)
2022-11-12 09:15:39.580 [Test worker] INFO kweb.Kweb - Shutting down Kweb
2022-11-12 09:15:39.586 [Test worker] INFO o.e.jetty.server.AbstractConnector - Stopped ServerConnector@4f824872{HTTP/1.1, (http/1.1, h2c)}{0.0.0.0:7660}
InputCheckedTest > checkBeforeAndAfterClick() FAILED
But in my opinion, it's bit complex and tricky way.
In my opinion, spring-boot users want to use the spring-boot's configuration loading feature and the bean management feature. But the sample code demonstrates the kweb integration with the spring-webmvc's servlet container.
This code demonstrates, the code starts kweb server and it runs with spring-boot's beans and configurations. While the shutdown process, spring will call the "close" method on the Kweb instance!
として cert-manager を入れる(digital ocean の web ui からも入れられるけど)
以下のようにして cert-manager を管理する。これも kubectl apply -f すれば OK
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# Email address used for ACME registration
email: tokuhirom@gmail.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Name of a secret used to store the ACME account private key
name: letsencrypt-prod-private-key
# Add a single challenge solver, HTTP01 using nginx
solvers:
- http01:
ingress:
class: nginx
s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s qq q and s s s chr length q q each die local chown rmdir tie chr q semop and s s s chr length q qr our xor getc dump order goto log join eof write time crypt dump exit vec index open ord tied q semop and s s s chr length q q each die local chown rmdir tie chr dump fcntl warn ord eval q semop and s s s chr length q qr our break chop given given pack q semop and s s s chr length q qr our break chop given given pack q semop and s s s chr length q qr our break chop given given pack exit ord goto read undef q semop and s s s chr length q qr our break chop given given pack exit ord goto read undef exp recv tell xor wait exit tell time bind tell each given q semop and s s s length q qr our break chop given given pack exit ord goto read undef exp recv tell xor wait exit tell time bind tell our log q semop and s s s chr length q qr our xor untie chomp lock chdir vec cmp pipe q semop and s s s length q qr our break chop given given pack exit ord goto read undef exp recv tell xor wait exit tell time q semop and s s s chr length q qr our xor untie chomp lock chdir vec cmp pipe q semop and s s s length q q each die local chown rmdir tie flock hex alarm undef cmp each ord glob ioctl untie die chr untie pack exp cmp given ref q semop and s s s chr length q qr our xor untie chomp lock chdir vec cmp pipe q semop and s s s length q q each die local chown rmdir tie q semop and s s s chr length q qr our xor untie chomp lock chdir vec cmp pipe q semop and s s s length q qr our break chop given given pack q semop and s s s chr length q qr our xor untie chomp lock chdir vec cmp pipe q semop and s s s length q q each die local chown rmdir tie flock hex alarm undef cmp each ord glob q semop and s s s chr length q qr our xor untie chomp lock chdir vec cmp pipe q semop and s s s length q q each die local chown rmdir tie flock hex alarm undef cmp each ord glob ioctl untie die chr link tie q semop and s s s chr length q qr our xor untie chomp lock chdir vec cmp pipe q semop and s s s length q qr our break chop given given pack exit ord goto read undef exp recv tell xor wait exit tell time bind fcntl q semop and s s s chr length q qr our xor untie chomp lock chdir vec cmp pipe q semop and s s s length q qr our break chop given given pack exit ord goto read undef exp recv tell xor wait exit tell time bind fcntl q semop and s s s chr length q qr our xor untie chomp lock chdir vec cmp pipe q semop and s s s length q q each die local chown rmdir tie flock hex alarm undef cmp each ord glob ioctl untie die chr untie pack exp cmp q semop and s s s chr length q qr our xor untie chomp lock chdir vec cmp pipe q semop and s s s length q qr our break chop given given pack q semop and eval eval
RUN cpanm Amon2
RUN cpanm DBD::SQLite LWP::Protocol::https DBD::mysql
RUN cpanm Catalyst::Runtime
RUN cpanm DBIx::Class
RUN cpanm Dist::Milla
RUN cpanm Tiffany
RUN cpanm Task::BeLike::TOKUHIROM
RUN cpanm Test::Perl::Critic Perl::Critic
RUN cpanm HTTP::Server::Simple::CGI Spiffy WWW::MobileCarrierJP
@file:DependsOn("org.jodd:jodd-http:6.2.1")
@file:DependsOn("org.jodd:jodd-lagarto:6.0.6")
import jodd.http.HttpRequest
import jodd.jerry.Jerry
data class Comment(
val id: String,
val user: String,
val time: String,
val comment: String,
val parent: String?,
)
var url = args[0]
val commentList = mutableListOf<Comment>()
while (true) {
println("Scraping $url")
// Send the HTTP request to our URL
val response = HttpRequest.get(url).send()
// Parse the HTML document into a Jerry DOM object
val doc = Jerry.of(response.bodyText())
// Find all comments <tr>s in the main comment table
val comments = doc.find("table.comment-tree tr.comtr")
// Iterate over each comment and extract its data
comments.forEach { element ->
val id = element.attr("id")
val user = element.find("a.hnuser").text()
val time = element.find("span.age").attr("title")
val comment = element.find("div.comment").text()
val parent = element.find("a:contains(parent)").attr("href")
// Append the data to comment_list
commentList.add(Comment(id, user, time, comment, parent))
}
// If there is a next link, set the URL and continue the while, otherwise exit
val next = doc.find("""a[rel="next"]""").attr("href")
if (next != null) url = "https://news.ycombinator.com/$next"
else break
}
println(commentList)
配布用パッケージの作成
kscript --package scraping.kts
とすると、java コマンドさえあれば実行できるバイナリを生成可能。このバイナリは mac でも linux でも動く。windows なら WSL の上でなら動くはず。
#!/usr/bin/env kscript
@file:DependsOn("com.offbytwo:docopt:0.6.0.20150202", "log4j:log4j:1.2.14")
import org.docopt.Docopt
import java.util.*
val usage = """
Use this cool tool to do cool stuff
Usage: cooltool.kts [options] <igenome> <fastq_files>...
Options:
--gtf <gtfFile> Custom gtf file instead of igenome bundled copy
--pc-only Use protein coding genes only for mapping and quantification
"""
val doArgs = Docopt(usage).parse(args.toList())
println("Hello from Kotlin!")
println("Parsed script arguments are: \n" + doArgs)
@file:DependsOn("com.github.holgerbrandl:kscript-support-api:1.2.5")
import kscript.text.*
val lines = resolveArgFile(args)
外部コマンドの実行
kutils を使うとかんたんに外部コマンドを実行できる。便利。
@file:DependsOn("com.github.holgerbrandl:kutils:0.12")
import de.mpicbg.scicomp.kutils.evalBash
val result = evalBash("date")
println(result.exitCode)
println(result.sout())