scala-webapp-template
scala-webapp-template copied to clipboard
General discussion
a@a-b:~/code/scala.js/scala-webapp-template$ sbt dev-web
a@b:~/scala-webapp-template$ sbt dev-web
Unrecognized VM option 'CMSClassUnloadingEnabled'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
copying runtime jar...
mkdir: cannot create directory ‘’: No such file or directory
Unrecognized VM option 'CMSClassUnloadingEnabled'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
Unrecognized VM option 'CMSClassUnloadingEnabled'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
I'm new to Scala and Java so I'm sure the answer should be obvious but I've been struggling with it so I thought I'd ask for some help.
I did some Googling and found that CMSClassUnloadingEnabled option was deprecated before Java 14. Is that true? What version of Java should I use?
https://stackoverflow.com/questions/70214788/unrecognized-vm-option-cmsclassunloadingenabled-after-switching-to-java-17
I tried this:
$ sdk use java 11.0.16-tem $ sbt dev-web
And got a about 8 warnings and some errors.
[info] welcome to sbt 1.6.2 (Eclipse Adoptium Java 11.0.16)
[info] loading settings for project global-plugins from plugins.sbt ...
[info] loading global plugins from /home/a/.sbt/1.0/plugins
[info] loading settings for project scala-webapp-template-build from plugins.sbt ...
[info] loading project definition from /home/a/code/scala.js/scala-webapp-template/project
[info] loading settings for project root from build.sbt ...
[info] BuildInfo settings:
[info] (apiUrl,None)
[info] BuildInfo settings:
[info] (apiUrl,None)
[info] set current project to root (in build file:/home/a/code/scala.js/scala-webapp-template/)
[warn] there are 8 keys that are not used by any other settings/tasks:
[warn]
[warn] * admin / Compile / fastOptJS / webpackExtraArgs
[warn] +- /home/a/code/scala.js/scala-webapp-template/build.sbt:199
[warn] * admin / Compile / fullOptJS / webpackExtraArgs
[warn] +- /home/a/code/scala.js/scala-webapp-template/build.sbt:200
[warn] * apiJS / Compile / stMinimize
[warn] +- /home/a/code/scala.js/scala-webapp-template/build.sbt:274
[warn] * commonJS / Compile / stMinimize
[warn] +- /home/a/code/scala.js/scala-webapp-template/build.sbt:249
[warn] * ui / Compile / stMinimize
[warn] +- /home/a/code/scala.js/scala-webapp-template/build.sbt:310
[warn] * web / Compile / fastOptJS / webpackExtraArgs
[warn] +- /home/a/code/scala.js/scala-webapp-template/build.sbt:199
[warn] * web / Compile / fullOptJS / webpackExtraArgs
[warn] +- /home/a/code/scala.js/scala-webapp-template/build.sbt:200
[warn] * web / Compile / stMinimize
[warn] +- /home/a/code/scala.js/scala-webapp-template/build.sbt:388
[warn]
[warn] note: a setting might still be used by a command; to exclude a key from this `lintUnused` check
[warn] either append it to `Global / excludeLintKeys` or call .withRank(KeyRanks.Invisible) on the key
[warn] sbt 0.13 shell syntax is deprecated; use slash syntax instead: web / Compile / fastOptJS / startWebpackDevServer
[info] Updating NPM dependencies
[info] Using lockfile /home/a/code/scala.js/scala-webapp-template/web/yarn.lock
[error] Usage: yarn [options]
[error] yarn: error: no such option: --non-interactive
[info] Updating NPM dependencies
[info] Using lockfile /home/a/code/scala.js/scala-webapp-template/lib/ui/yarn.lock
[error] Usage: yarn [options]
[error] yarn: error: no such option: --non-interactive
[error] java.lang.RuntimeException: Non-zero exit code: 2
[error] at scala.sys.package$.error(package.scala:30)
[error] at scalajsbundler.util.Commands$.$anonfun$run$9(Commands.scala:38)
[error] at scala.util.Either.fold(Either.scala:192)
[error] at scalajsbundler.util.Commands$.run(Commands.scala:38)
[error] at scalajsbundler.ExternalCommand.run(ExternalCommand.scala:22)
[error] at scalajsbundler.ExternalCommand$.$anonfun$install$1(ExternalCommand.scala:90)
[error] at scalajsbundler.ExternalCommand$.syncYarnLockfile(ExternalCommand.scala:46)
[error] at scalajsbundler.ExternalCommand$.install(ExternalCommand.scala:89)
[error] at scalajsbundler.sbtplugin.NpmUpdateTasks$.$anonfun$npmInstallDependencies$1(NpmUpdateTasks.scala:59)
[error] at sbt.util.FileFunction$.$anonfun$cached$1(FileFunction.scala:80)
[error] at sbt.util.FileFunction$.$anonfun$cached$4(FileFunction.scala:153)
[error] at sbt.util.Difference.apply(Tracked.scala:414)
[error] at sbt.util.Difference.apply(Tracked.scala:394)
[error] at sbt.util.FileFunction$.$anonfun$cached$3(FileFunction.scala:149)
[error] at sbt.util.Difference.apply(Tracked.scala:414)
[error] at sbt.util.Difference.apply(Tracked.scala:389)
[error] at sbt.util.FileFunction$.$anonfun$cached$2(FileFunction.scala:148)
[error] at scalajsbundler.sbtplugin.NpmUpdateTasks$.npmInstallDependencies(NpmUpdateTasks.scala:62)
[error] at scalajsbundler.sbtplugin.ScalaJSBundlerPlugin$.$anonfun$perConfigSettings$7(ScalaJSBundlerPlugin.scala:650)
[error] at scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error] at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:62)
[error] at sbt.std.Transform$$anon$4.work(Transform.scala:68)
[error] at sbt.Execute.$anonfun$submit$2(Execute.scala:282)
[error] at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
[error] at sbt.Execute.work(Execute.scala:291)
[error] at sbt.Execute.$anonfun$submit$1(Execute.scala:282)
[error] at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
[error] at sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
[error] at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error] at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
[error] at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[error] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[error] at java.base/java.lang.Thread.run(Thread.java:829)
[error] java.lang.RuntimeException: Non-zero exit code: 2
[error] at scala.sys.package$.error(package.scala:30)
[error] at scalajsbundler.util.Commands$.$anonfun$run$9(Commands.scala:38)
[error] at scala.util.Either.fold(Either.scala:192)
[error] at scalajsbundler.util.Commands$.run(Commands.scala:38)
[error] at scalajsbundler.ExternalCommand.run(ExternalCommand.scala:22)
[error] at scalajsbundler.ExternalCommand$.$anonfun$install$1(ExternalCommand.scala:90)
[error] at scalajsbundler.ExternalCommand$.syncYarnLockfile(ExternalCommand.scala:46)
[error] at scalajsbundler.ExternalCommand$.install(ExternalCommand.scala:89)
[error] at scalajsbundler.sbtplugin.NpmUpdateTasks$.$anonfun$npmInstallDependencies$1(NpmUpdateTasks.scala:59)
[error] at sbt.util.FileFunction$.$anonfun$cached$1(FileFunction.scala:80)
[error] at sbt.util.FileFunction$.$anonfun$cached$4(FileFunction.scala:153)
[error] at sbt.util.Difference.apply(Tracked.scala:414)
[error] at sbt.util.Difference.apply(Tracked.scala:394)
[error] at sbt.util.FileFunction$.$anonfun$cached$3(FileFunction.scala:149)
[error] at sbt.util.Difference.apply(Tracked.scala:414)
[error] at sbt.util.Difference.apply(Tracked.scala:389)
[error] at sbt.util.FileFunction$.$anonfun$cached$2(FileFunction.scala:148)
[error] at scalajsbundler.sbtplugin.NpmUpdateTasks$.npmInstallDependencies(NpmUpdateTasks.scala:62)
[error] at scalajsbundler.sbtplugin.ScalaJSBundlerPlugin$.$anonfun$perConfigSettings$7(ScalaJSBundlerPlugin.scala:650)
[error] at scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error] at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:62)
[error] at sbt.std.Transform$$anon$4.work(Transform.scala:68)
[error] at sbt.Execute.$anonfun$submit$2(Execute.scala:282)
[error] at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
[error] at sbt.Execute.work(Execute.scala:291)
[error] at sbt.Execute.$anonfun$submit$1(Execute.scala:282)
[error] at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
[error] at sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
[error] at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error] at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
[error] at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[error] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[error] at java.base/java.lang.Thread.run(Thread.java:829)
[error] (ui / Compile / npmInstallDependencies) Non-zero exit code: 2
[error] (web / Compile / npmInstallDependencies) Non-zero exit code: 2
[error] Total time: 1 s, completed Sep 27, 2022, 7:00:50 PM
I really haven't tried with jdk 17 but there seems to be an unknown jvm option involved.
The recommended way is to leverage https://sdkman.io/ and pick the jdk from https://github.com/wiringbits/scala-webapp-template/blob/master/.sdkmanrc (which seems out of date), java 11.0.16-tem
should work.
sbt dev-web
It does print some warnings but the logs you shared show the problem:
[error] Usage: yarn [options]
[error] yarn: error: no such option: --non-interactive
I'd bet you are using an incompatible yarn version, I'd suggest to use https://github.com/nvm-sh/nvm to pick the supported node version (https://github.com/wiringbits/scala-webapp-template/blob/master/.nvmrc), then, install yarn (I have 1.22.11
).
If you get more issues, feel free to comment, I'm in the process to update the docs.
@nickcoast please check the docs and let us know if you get into any other issues: https://github.com/wiringbits/scala-webapp-template/blob/master/docs/setup-dev-environment.md
EDIT: I see you added some docs since the time I started composing this reply. I will take a look. Thanks again.
Thank you so much! Your suggestions helped me get past those issues and get this working.
Just in case it might be useful for your documentation, I'll note a couple problems I worked through . Though they may be basic and obvious to others.
-
yarn: I had to use
global
option to fix theyarn
problem.npm install --global yarn
. -
postgres: Pretty sure I ran this query,
CREATE EXTENSION IF NOT EXISTS CITEXT
, during set up, but got error onlocalhost:9000
, Ran the query again and clicked the 'resolve' - no change. Nouser*
tables created. Finally, deletedplay_evolutions
table and restarted the Scala server, and all tables got created. -
Creating a new account worked (db records were added), but I received no confirmation on the frontend (still shows filled in form - see screenshot below). I assume that's because of failure to communicate with the AWS service that would exist in production. I ran the query
UPDATE users SET verified_on = '2022-09-29';
and now login works.
Here's the new account error:
software.amazon.awssdk.services.ses.model.SesException: The security token included in the request is invalid. (Service: Ses, Status Code: 403, Request ID: 0974de2c-0ab8-463b-a806-f34fbd2b4f93)
Here's the form after submission, with the AWS error:
Also, the Ubuntu Setup section was helpful but, perhaps naively, I assumed all other dependencies would be handled in the build process. As you pointed out, I needed the right node version to get yarn to work. But again, maybe that's just me not having enough experience with these technologies and maybe your target audience is more savvy.
Thanks again!
Thanks for getting back and sharing the useful insights, I'll take your suggestions and update the docs again.
yarn: I had to use global option to fix the yarn problem. npm install --global yarn.
I included this a couple of hours ago.
postgres: Pretty sure I ran this query, CREATE EXTENSION IF NOT EXISTS CITEXT, during set up, but got error on localhost:9000, Ran the query again and clicked the 'resolve' - no change. No user* tables created. Finally, deleted play_evolutions table and restarted the Scala server, and all tables got created.
People commonly fails to connect to the database before creating the extension, there is a chance you experienced the same.
Creating a new account worked (db records were added), but I received no confirmation on the frontend (still shows filled in form - see screenshot below). I assume that's because of failure to communicate with the AWS service that would exist in production. I ran the query UPDATE users SET verified_on = '2022-09-29'; and now login works.
Oh, good catch! most emails are sent through a background job but it seems this is not the case when you are creating an account.
Also, the Ubuntu Setup section was helpful but, perhaps naively, I assumed all other dependencies would be handled in the build process. As you pointed out, I needed the right node version to get yarn to work. But again, maybe that's just me not having enough experience with these technologies and maybe your target audience is more savvy.
Which section are you referring to?
all other dependencies would be handled in the build process
There are some native dependencies required, I tried making that clear on the new docs, yarn
was particularly hidden.
maybe your target audience is more savvy.
I'm actually trying to make the process as simple as possible, we have had this project mostly for internal usage, hence, the support has been handled through our internal chats.
Oh, good catch! most emails are sent through a background job but it seems this is not the case when you are creating an account.
Now I finally remember why it was done this way, most emails are sent through the background job unless they include sensitive information, for example, the token used to verify an email or to recover a password, I created https://github.com/wiringbits/scala-webapp-template/pull/285 to simplify this process, now, there are no more errors when an account is created but AWS is not used.
That makes sense, works great.
Your new docs for Architecture are extremely helpful. I've added some models but am was getting a 500 Internal Server error
when running server/run
and I'm wondering if you can help me find a log that would show me the error text. localhost:9000
was showing some useful information, but after I added some more code I'm just getting a 500 Internal Server Error
.
Nothing is logged in ~.logs/application.log
, nor in the Postgresql log, for this error.
The error went away after I added 2.sql
evolution, but I would still like to know where I can find error information. Thank you!
https://github.com/nickcoast/fbn-scala
Also, by the way, in case you will be adding any IDE docs, I will share a couple things I found helpful in IntelliJ. I created 1 run configuration each for server/run
, dev-web
, and dev-admin
and combined them under a "Compound" configuration that runs all 3 at once. Before that, I was running one in IntelliJ's sbt console, and the other two in IntelliJ terminal windows, which didn't seem like the right way to do it.
Also, I added *.iml
to .gitignore
to ignore IntelliJ's files, and added .envrc
to ignore the direnv
file from your docs.
Your new docs for Architecture are extremely helpful
Glad you liked that, there is still work to be done to adapt the code to make such separations clear.
I've added some models but am was getting a 500 Internal Server error when running server/run and I'm wondering if you can help me find a log that would show me the error text. localhost:9000 was showing some useful information, but after I added some more code I'm just getting a 500 Internal Server Error.
Logs are printed to stdout, could it be anything related to your compound IDE settings? I tend to run the app by launching 3 terminals, the one where I execute sbt server/run
prints the logs, in any case, file logs should be found at ~/logs/application.log
(see logback.xml)
The error went away after I added 2.sql evolution, but I would still like to know where I can find error information. Thank you!
This is actually weird, I'd bet there is something else going on but it is hard to tell without logs.
I see you got into some trouble with sql parsers, feel free to ask questions about those.
Thank you!
Actually it turns my 2.sql
evolution did not cause the change in the failure mode. It was a new syntax syntax error from when I git merged, and I guess the web interface for Scala Play stopped at the syntax error. After I fixed it, the 500 error came back, but only if I added my new implicit val
Case Class parsers to daos/package.scala
. (If commented them out, a different Scala Play error page came up) I am guessing that it's because the parsers rely on bytecode
and my IDE did not recompile it yet. Does that sound right?
I don't know if my parsers, etc, are really working until I try some read/write but at least there are no compile or runtime errors yet.
So, the server seems to be working and my next steps will be creating new widgets and/or pages.
By the way, the evolutions
error happened because I already created my tables, so evolutions
could not CREATE TABLE
. I clicked "resolved" on the web interface but nothing changed. I had to DROP
my tables, run DELETE FROM play_evolutions WHERE id = 2
, and restart the server so that 2.sql
could be reapplied and marked successful in the play_evolutions
table. Is that normal?
Thanks so much for your help!
Actually it turns my 2.sql evolution did not cause the change in the failure mode
Which makes sense, in any case, I'd expect it to cause a failure.
After I fixed it, the 500 error came back, but only if I added my new implicit val Case Class parsers to daos/package.scala. (If commented them out, a different Scala Play error page came up) I am guessing that it's because the parsers rely on bytecode and my IDE did not recompile it yet. Does that sound right?
It is hard to know without logs.
I don't know if my parsers, etc, are really working until I try some read/write but at least there are no compile or runtime errors yet.
Usually, you write a simple test to verify that (check tests for repositories).
By the way, the evolutions error happened because I already created my tables, so evolutions could not CREATE TABLE. I clicked "resolved" on the web interface but nothing changed. I had to DROP my tables, run DELETE FROM play_evolutions WHERE id = 2, and restart the server so that 2.sql could be reapplied and marked successful in the play_evolutions table. Is that normal?
Usually, the UI should work but I tend to update play_evolutions
table directly when such details occur, same way, repository tests help you to make sure evolutions can be applied correctly.
Hi Alex,
I added some routes, a controller, actions, repos. I'm getting an error related to java.time.Instant
but the error output doesn't seem to point to anywhere in my code. Do you have any tips on how I could debug this?
Thank you
Output from sbt server/test
nick@asdf:~/code/scala.js/fbn$ sbt server/test
[info] welcome to sbt 1.6.2 (Eclipse Adoptium Java 11.0.16)
[info] loading settings for project global-plugins from plugins.sbt ...
[info] loading global plugins from /home/nick/.sbt/1.0/plugins
[info] loading settings for project fbn-build from plugins.sbt ...
[info] loading project definition from /home/nick/code/scala.js/fbn/project
[info] loading settings for project root from build.sbt ...
[info] BuildInfo settings:
[info] (apiUrl,None)
[info] BuildInfo settings:
[info] (apiUrl,None)
[info] set current project to root (in build file:/home/nick/code/scala.js/fbn/)
[warn] there are 8 keys that are not used by any other settings/tasks:
[warn]
[warn] * admin / Compile / fastOptJS / webpackExtraArgs
[warn] +- /home/nick/code/scala.js/fbn/build.sbt:199
[warn] * admin / Compile / fullOptJS / webpackExtraArgs
[warn] +- /home/nick/code/scala.js/fbn/build.sbt:200
[warn] * apiJS / Compile / stMinimize
[warn] +- /home/nick/code/scala.js/fbn/build.sbt:274
[warn] * commonJS / Compile / stMinimize
[warn] +- /home/nick/code/scala.js/fbn/build.sbt:249
[warn] * ui / Compile / stMinimize
[warn] +- /home/nick/code/scala.js/fbn/build.sbt:310
[warn] * web / Compile / fastOptJS / webpackExtraArgs
[warn] +- /home/nick/code/scala.js/fbn/build.sbt:199
[warn] * web / Compile / fullOptJS / webpackExtraArgs
[warn] +- /home/nick/code/scala.js/fbn/build.sbt:200
[warn] * web / Compile / stMinimize
[warn] +- /home/nick/code/scala.js/fbn/build.sbt:388
[warn]
[warn] note: a setting might still be used by a command; to exclude a key from this `lintUnused` check
[warn] either append it to `Global / excludeLintKeys` or call .withRank(KeyRanks.Invisible) on the key
[info] compiling 3 Scala sources to /home/nick/code/scala.js/fbn/server/target/scala-2.13/classes ...
java.lang.ClassNotFoundException: Instant
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:476)
| => sat java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at io.swagger.util.ReflectionUtils.loadClassByName(ReflectionUtils.java:53)
at io.swagger.jackson.ModelResolver.getInnerType(ModelResolver.java:932)
at io.swagger.jackson.ModelResolver.resolve(ModelResolver.java:468)
at io.swagger.jackson.ModelResolver.resolve(ModelResolver.java:203)
at io.swagger.scala.converter.SwaggerScalaModelConverter.resolve(SwaggerScalaModelConverter.scala:90)
at com.github.dwickern.swagger.ValidationModelConverter.resolve(ValidationModelConverter.scala:63)
at io.swagger.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:103)
at io.swagger.jackson.ModelResolver.resolve(ModelResolver.java:257)
at io.swagger.jackson.ModelResolver.resolve(ModelResolver.java:203)
at io.swagger.scala.converter.SwaggerScalaModelConverter.resolve(SwaggerScalaModelConverter.scala:90)
at com.github.dwickern.swagger.ValidationModelConverter.resolve(ValidationModelConverter.scala:63)
at io.swagger.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:103)
at io.swagger.jackson.ModelResolver.resolveProperty(ModelResolver.java:175)
at io.swagger.jackson.ModelResolver.resolveProperty(ModelResolver.java:126)
at io.swagger.scala.converter.SwaggerScalaModelConverter.resolveProperty(SwaggerScalaModelConverter.scala:70)
at com.github.dwickern.swagger.ValidationModelConverter.resolveProperty(ValidationModelConverter.scala:59)
at io.swagger.converter.ModelConverterContextImpl.resolveProperty(ModelConverterContextImpl.java:83)
at io.swagger.converter.ModelConverters.readAsProperty(ModelConverters.java:63)
at io.swagger.converter.ModelConverters.readAsProperty(ModelConverters.java:57)
at io.swagger.util.ParameterProcessor.applyAnnotations(ParameterProcessor.java:263)
at play.modules.swagger.PlayReader.readImplicitParam(PlayReader.java:441)
at play.modules.swagger.PlayReader.readImplicitParameters(PlayReader.java:408)
at play.modules.swagger.PlayReader.read(PlayReader.java:208)
at play.modules.swagger.PlayReader.read(PlayReader.java:76)
at play.modules.swagger.PlayReader.read(PlayReader.java:70)
at play.modules.swagger.ApiListingCache.$anonfun$listing$1(ApiListingCache.scala:17)
at scala.collection.mutable.HashMap.getOrElseUpdate(HashMap.scala:454)
at play.modules.swagger.ApiListingCache.listing(ApiListingCache.scala:13)
at com.github.dwickern.swagger.SwaggerRunner$.run(SwaggerRunner.scala:31)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at com.github.dwickern.sbt.SwaggerPlayPlugin$.$anonfun$projectSettings$12(SwaggerPlayPlugin.scala:87)
at com.github.dwickern.sbt.SwaggerPlayPlugin$$anon$2.run(SwaggerPlayPlugin.scala:172)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at com.github.dwickern.sbt.SwaggerPlayPlugin$.withContextClassLoader(SwaggerPlayPlugin.scala:175)
at com.github.dwickern.sbt.SwaggerPlayPlugin$.$anonfun$projectSettings$11(SwaggerPlayPlugin.scala:79)
at scala.Function1.$anonfun$compose$1(Function1.scala:49)
at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:62)
at sbt.std.Transform$$anon$4.work(Transform.scala:68)
at sbt.Execute.$anonfun$submit$2(Execute.scala:282)
at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
at sbt.Execute.work(Execute.scala:291)
at sbt.Execute.$anonfun$submit$1(Execute.scala:282)
at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
at sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)
[error] java.lang.IllegalArgumentException: argument "t" is null
[error] at com.fasterxml.jackson.databind.ObjectMapper._assertNotNull(ObjectMapper.java:4737)
[error] at com.fasterxml.jackson.databind.ObjectMapper.constructType(ObjectMapper.java:2116)
[error] at io.swagger.scala.converter.SwaggerScalaModelConverter.resolveProperty(SwaggerScalaModelConverter.scala:24)
[error] at com.github.dwickern.swagger.ValidationModelConverter.resolveProperty(ValidationModelConverter.scala:59)
[error] at io.swagger.converter.ModelConverterContextImpl.resolveProperty(ModelConverterContextImpl.java:83)
[error] at io.swagger.jackson.ModelResolver.resolve(ModelResolver.java:469)
[error] at io.swagger.jackson.ModelResolver.resolve(ModelResolver.java:203)
[error] at io.swagger.scala.converter.SwaggerScalaModelConverter.resolve(SwaggerScalaModelConverter.scala:90)
[error] at com.github.dwickern.swagger.ValidationModelConverter.resolve(ValidationModelConverter.scala:63)
[error] at io.swagger.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:103)
[error] at io.swagger.jackson.ModelResolver.resolve(ModelResolver.java:257)
[error] at io.swagger.jackson.ModelResolver.resolve(ModelResolver.java:203)
[error] at io.swagger.scala.converter.SwaggerScalaModelConverter.resolve(SwaggerScalaModelConverter.scala:90)
[error] at com.github.dwickern.swagger.ValidationModelConverter.resolve(ValidationModelConverter.scala:63)
[error] at io.swagger.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:103)
[error] at io.swagger.jackson.ModelResolver.resolveProperty(ModelResolver.java:175)
[error] at io.swagger.jackson.ModelResolver.resolveProperty(ModelResolver.java:126)
[error] at io.swagger.scala.converter.SwaggerScalaModelConverter.resolveProperty(SwaggerScalaModelConverter.scala:70)
[error] at com.github.dwickern.swagger.ValidationModelConverter.resolveProperty(ValidationModelConverter.scala:59)
[error] at io.swagger.converter.ModelConverterContextImpl.resolveProperty(ModelConverterContextImpl.java:83)
[error] at io.swagger.converter.ModelConverters.readAsProperty(ModelConverters.java:63)
[error] at io.swagger.converter.ModelConverters.readAsProperty(ModelConverters.java:57)
[error] at io.swagger.util.ParameterProcessor.applyAnnotations(ParameterProcessor.java:263)
[error] at play.modules.swagger.PlayReader.readImplicitParam(PlayReader.java:441)
[error] at play.modules.swagger.PlayReader.readImplicitParameters(PlayReader.java:408)
[error] at play.modules.swagger.PlayReader.read(PlayReader.java:208)
[error] at play.modules.swagger.PlayReader.read(PlayReader.java:76)
[error] at play.modules.swagger.PlayReader.read(PlayReader.java:70)
[error] at play.modules.swagger.ApiListingCache.$anonfun$listing$1(ApiListingCache.scala:17)
[error] at scala.collection.mutable.HashMap.getOrElseUpdate(HashMap.scala:454)
[error] at play.modules.swagger.ApiListingCache.listing(ApiListingCache.scala:13)
[error] at com.github.dwickern.swagger.SwaggerRunner$.run(SwaggerRunner.scala:31)
[error] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[error] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[error] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[error] at java.base/java.lang.reflect.Method.invoke(Method.java:566)
[error] at com.github.dwickern.sbt.SwaggerPlayPlugin$.$anonfun$projectSettings$12(SwaggerPlayPlugin.scala:87)
[error] at com.github.dwickern.sbt.SwaggerPlayPlugin$$anon$2.run(SwaggerPlayPlugin.scala:172)
[error] at java.base/java.security.AccessController.doPrivileged(Native Method)
[error] at com.github.dwickern.sbt.SwaggerPlayPlugin$.withContextClassLoader(SwaggerPlayPlugin.scala:175)
[error] at com.github.dwickern.sbt.SwaggerPlayPlugin$.$anonfun$projectSettings$11(SwaggerPlayPlugin.scala:79)
[error] at scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error] at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:62)
[error] at sbt.std.Transform$$anon$4.work(Transform.scala:68)
[error] at sbt.Execute.$anonfun$submit$2(Execute.scala:282)
[error] at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
[error] at sbt.Execute.work(Execute.scala:291)
[error] at sbt.Execute.$anonfun$submit$1(Execute.scala:282)
[error] at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
[error] at sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
[error] at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error] at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
[error] at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[error] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[error] at java.base/java.lang.Thread.run(Thread.java:829)
[error] (server / swaggerPlayJson) java.lang.IllegalArgumentException: argument "t" is null
[error] Total time: 9 s, completed Oct 8, 2022, 12:51:06 PM
nick@asdf:~/code/scala.js/fbn$
Oh, I have saw similar errors, please remove any swagger annotations you changed/added to your controller, there are tricky cases that I must document.
I've made a lot of progress since removing those annotations. They were indeed causing that issue.
Could you please give me a hint about creating a modal dialog in scala.js? Do I need to add a new library to build.sbt?
Thank you much
The project already has everything from https://v3.mui.com/ available, in particular, check dialogs, I have another project where such a component is used.
Good luck!
Thanks! One more quick question if you have the time.
Should I be using UUID primary keys for all DB tables? Seems the Admin interface only allows viewing individual records for tables with UUID primary keys.
Should I be using UUID primary keys for all DB tables? Seems the Admin interface only allows viewing individual records for tables with UUID primary keys.
It's been a while since I checked the react-admin backend we built but I wouldn't be surprised if that's the case, most of the work we are doing on top of this template defaults to UUID
primary keys.
I'm documenting the project slowly, some day, I'll get to the admin integration, in the mean time, feel free to raise any other questions.
Thank! For deployment, there's lots of sensitive data that needs to go in /infra/
. For a project based on your repo (like mine is), would you add the whole directory to .gitignore
? Or remove it from the repo and perhaps commit it to a private repo?
I commonly use any of these approaches:
- Add prod details to gitignore.
- Use ansible-vault to keep those encrypted.
Great, thanks, I will try the ansible-vault. Seems like a good solution.
EDIT:
I had some question but made some progress. Deleting for now. Made some progress in debugging. Issues with environment variables and 'host not allowed' warnings.
I commented out the frontend value and then ansible deployed my server.yml to the backend value.
This seems weird.
I'm getting this error when I try to start the wiringbits server on either of my EC2 instances:
Check logs (home/play/logs/application.log
), most likely reason is that server can't connect to the database.
EDIT: seems to be postgres authentication problem. I thought I created the user and db on both of my test servers but now user is not found. I'll try to fix.
Indeed.
in infra/config/server/dev.env.j2 I see some variables that are coming from the ini file. Can I also put the POSTGRES_PASSWORD and other values as variables in dev.env.js and declare them in the ini file? I've tried doing that but I'm not sure it's working.
While it is doable, I wouldn't recommend you to do that, backend settings can be encrypted with ansible-vault while hosts file is usully tracked by git
If you check the backend settings, there is a line PLAY_SESSION_DOMAIN="{{ web_app_domain }}"
which is set at the hosts file web_app_domain="template-demo.wiringbits.net"
.
At last, web_app_site.j2
needs to be updated to reflect where your backend instances are located.
Could there be some problem loading the environment variables with the wiringbits-server
service?
I tried running the wiringbits-server
executable in /home/play/app/wiringbits-server-0.1.0-SNAPSHOT/bin
and it gave postgres authentication error. Then I loaded all the environment variables from /home/play/app/.env
ran wiringbits-server
without error, and was able to get a response from port :9000
{"error":{"requestId":3,"message":"Host not allowed: xx.xxx.xx.xx:9000"}}
(server is responding!)
I can also log in to Posgresql from cli with the user name/password from .env
, so they are correct.
ubuntu@asdf:/etc/systemd/system$ psql -U db_user -h 127.0.0.1 server_db
Password for user db_user:
psql (14.5 (Ubuntu 14.5-0ubuntu0.22.04.1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.
/etc/systemd/system/wiringbits-server.service
has the correct .env
file:
[Unit]
Description=wiringbits-server
[Service]
Type=simple
WorkingDirectory=/home/play/app/wiringbits-server-0.1.0-SNAPSHOT.zip
StandardOutput=tty
StandardError=tty
EnvironmentFile=/home/play/app/.env
LimitNOFILE=65535
User=play
ExecStart=/home/play/app/wiringbits-server-0.1.0-SNAPSHOT.zip/bin/wiringbits-server -Dpidfile.path=/dev/null
Restart=on-failure
[Install]
WantedBy=multi-user.target
That would be weird but I have experienced something similar in different ubuntu versions, when the app starts, it prints logs with the loaded settings, can you please check those and verify if the variables are being picked up?
It turned out I had a typo in my app_directory_name
in my hosts.ini
file. I found using journalctl -u wiringbits-server
that it couldn't find the binary! So, that explains why there were no logs created for the app, since it was never able to be run that way. Whoops!
In your demo-hosts.ini
file you have this (with same IP for both servers).
[backend]
test-server ansible_host=64.227.100.33
[backend:vars]
# this is where the environment variables required by the app are defined
# it could be kept encrypted by using ansible-vault
app_env_config_source=config/server/demo.env.j2
.
.
.
[frontend]
test-server ansible_host=64.227.100.33
[frontend:vars]
# this is necessary to get SSL certificates, it is the email for receiving notifications from letsencrypt
[email protected]
.
.
.
It seems that test-server
causes a conflict. The second test-server
overwrites the first, which only matters if it's 2 different machines. So when I run my hosts.ini
with two different machines:
ansible-playbook -i hosts.ini server.yml web.yml admin.yml
...everything gets deployed to the second test-server
.
I changed the name under [backend]
to back-server
and now ansible
has deployed the server app to the right EC2 instance, and the web
and admin
apps to the other EC2 instance as I intended.
Is it not supposed to work that way? Ansible docs seem to suggest that ansible_*
variables are global. Perhaps that's why?
I did some experiments. Using the same name ("test-server") causes the frontend and backend to all get merged under test-server
.
Here I am running this command with the ansible_host
under [frontend]
having IP ending in 66, and the [backend]
ending in 33 is not there!:
nick@nick:~/code/scala.js/fbn/infra$ ansible-inventory -i demo-hosts.ini server.yml --list
{
"_meta": {
"hostvars": {
"test-server": {
"admin_api_url": "https://template-demo-admin.wiringbits.net/api",
"admin_app_domain": "template-demo-admin.wiringbits.net",
"ansible_host": "64.227.100.66",
"ansible_ssh_extra_args": "-o StrictHostKeyChecking=no",
"ansible_user": "ubuntu",
"app_directory_name": "wiringbits-server-0.1.0-SNAPSHOT",
"app_env_config_source": "config/server/demo.env.j2",
"app_source_name": "wiringbits-server-0.1.0-SNAPSHOT.zip",
"app_startup_script": "wiringbits-server",
"app_systemd_service_name": "wiringbits-server",
"app_systemd_service_source": "config/server/server.service.j2",
"letsencrypt_notifications_email": "[email protected]",
"nginx_admin_password_file": "config/nginx/admin-app-htpasswd",
"web_api_url": "https://template-demo.wiringbits.net/api",
"web_app_domain": "template-demo.wiringbits.net"
}
}
},
"all": {
"children": [
"ungrouped",
"webapp"
]
},
"backend": {
"hosts": [
"test-server"
]
},
"frontend": {
"hosts": [
"test-server"
]
},
"webapp": {
"children": [
"backend",
"frontend"
]
}
}
And now again, after changing test-server
to back-server
under [backend]
, now both ips appear under their own sections:
nick@nick:~/code/scala.js/fbn/infra$ ansible-inventory -i demo-hosts.ini server.yml --list
{
"_meta": {
"hostvars": {
"back-server": {
"admin_app_domain": "template-demo-admin.wiringbits.net",
"ansible_host": "64.227.100.33",
"ansible_ssh_extra_args": "-o StrictHostKeyChecking=no",
"ansible_user": "ubuntu",
"app_directory_name": "wiringbits-server-0.1.0-SNAPSHOT",
"app_env_config_source": "config/server/demo.env.j2",
"app_source_name": "wiringbits-server-0.1.0-SNAPSHOT.zip",
"app_startup_script": "wiringbits-server",
"app_systemd_service_name": "wiringbits-server",
"app_systemd_service_source": "config/server/server.service.j2",
"web_app_domain": "template-demo.wiringbits.net"
},
"test-server": {
"admin_api_url": "https://template-demo-admin.wiringbits.net/api",
"admin_app_domain": "template-demo-admin.wiringbits.net",
"ansible_host": "64.227.100.66",
"ansible_ssh_extra_args": "-o StrictHostKeyChecking=no",
"ansible_user": "ubuntu",
"letsencrypt_notifications_email": "[email protected]",
"nginx_admin_password_file": "config/nginx/admin-app-htpasswd",
"web_api_url": "https://template-demo.wiringbits.net/api",
"web_app_domain": "template-demo.wiringbits.net"
}
}
},
"all": {
"children": [
"ungrouped",
"webapp"
]
},
"backend": {
"hosts": [
"back-server"
]
},
"frontend": {
"hosts": [
"test-server"
]
},
"webapp": {
"children": [
"backend",
"frontend"
]
}
}
Maybe this is all due to some other mistake I've made but changing one test-server
to a different name seems to have fixed my deployment issue.
It seems that test-server causes a conflict. The second test-server overwrites the first, which only matters if it's 2 different machines. So when I run my hosts.ini with two different machines:
I'd say that you basically found a bug in the scripts, thanks for sharing your details! I checked another project that uses similar ansible scripts but deploying to many instances, and, each server has a different variable name.
Given that this has become a general discussion, I'm reopening and pinning the ticket.
Previously, I mentioned that current swagger integration is tricky, I added some docs just in case you are interested in this.
If you have questions about any other topic, please let me know to prioritize documenting those topics.
In DatabaseTablesDAO.scala
in package package net.wiringbits.webapp.utils.admin.repositories.daos
, def find
requires UUID primary key. Is there a way I can change it to also accept integer primary keys?
I would tweak it myself but I'm not sure how to deal with compiled sources (I'm new to Java).
def find(tableName: String, primaryKeyField: String, primaryKeyValue: String)(implicit
conn: Connection
): Option[TableRow] = {
val sql = s"""
SELECT *
FROM $tableName
WHERE $primaryKeyField = ?
"""
val preparedStatement = conn.prepareStatement(sql)
// TODO: UUID from String can fail if the ID field isn't an UUID
preparedStatement.setObject(1, UUID.fromString(primaryKeyValue))
val resultSet = preparedStatement.executeQuery()
Try {
resultSet.next()
val numberOfColumns = resultSet.getMetaData.getColumnCount
val row = for {
columnNumber <- 1 to numberOfColumns
cellData = resultSet.getString(columnNumber)
} yield Cell(Option(cellData).getOrElse(""))
TableRow(row.toList)
}.toOption
}
We believe that there shouldn't be so many changes involved, still, the sources are not in this repository, they are at https://github.com/wiringbits/wiringbits-webapp-utils
A simple solution would be to use the module settings to specify the type for the primary key field, then, we can update queries accordingly.
@nickcoast I have created https://github.com/wiringbits/wiringbits-webapp-utils/issues/138 to track this improvement.
Deployment question.
I changed my subdomain in my hosts.ini for the web app and redeployed now I'm getting 502 Bad Gateway
response for requests to the web and admin api urls. And then when I changed back to the previous subdomain, still happening.
So I can load the homepage of the web app, but api requests fail. Can't sign in anymore. And admin is blank since rendering depends on the api response.
Do you think this is an nginx config issue?
Did you ran the nginx_site_web.yml
and nginx_site_admin.yml
playbooks? because that can be the problem. These scripts need to be executed after changing domains.