scala-webapp-template icon indicating copy to clipboard operation
scala-webapp-template copied to clipboard

General discussion

Open nickcoast opened this issue 2 years ago • 41 comments

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

nickcoast avatar Sep 28 '22 01:09 nickcoast

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.

AlexITC avatar Sep 28 '22 02:09 AlexITC

@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

AlexITC avatar Sep 30 '22 23:09 AlexITC

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 the yarn problem. npm install --global yarn.

  • 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.

  • 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: image

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!

nickcoast avatar Oct 01 '22 01:10 nickcoast

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.

AlexITC avatar Oct 01 '22 01:10 AlexITC

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.

AlexITC avatar Oct 01 '22 03:10 AlexITC

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.

nickcoast avatar Oct 06 '22 04:10 nickcoast

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.

AlexITC avatar Oct 06 '22 05:10 AlexITC

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!

nickcoast avatar Oct 06 '22 07:10 nickcoast

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.

AlexITC avatar Oct 06 '22 15:10 AlexITC

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$ 

nickcoast avatar Oct 08 '22 20:10 nickcoast

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.

AlexITC avatar Oct 08 '22 20:10 AlexITC

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

nickcoast avatar Oct 13 '22 20:10 nickcoast

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!

AlexITC avatar Oct 13 '22 20:10 AlexITC

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.

nickcoast avatar Oct 13 '22 22:10 nickcoast

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.

AlexITC avatar Oct 13 '22 23:10 AlexITC

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?

nickcoast avatar Oct 14 '22 07:10 nickcoast

I commonly use any of these approaches:

  1. Add prod details to gitignore.
  2. Use ansible-vault to keep those encrypted.

AlexITC avatar Oct 14 '22 14:10 AlexITC

Great, thanks, I will try the ansible-vault. Seems like a good solution.

nickcoast avatar Oct 14 '22 18:10 nickcoast

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.

nickcoast avatar Oct 22 '22 22:10 nickcoast

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.

AlexITC avatar Oct 23 '22 14:10 AlexITC

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

nickcoast avatar Oct 24 '22 04:10 nickcoast

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?

AlexITC avatar Oct 24 '22 04:10 AlexITC

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.

nickcoast avatar Oct 26 '22 07:10 nickcoast

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.

AlexITC avatar Oct 26 '22 13:10 AlexITC

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.

AlexITC avatar Oct 27 '22 02:10 AlexITC

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
  }

nickcoast avatar Oct 31 '22 10:10 nickcoast

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.

AlexITC avatar Oct 31 '22 14:10 AlexITC

@nickcoast I have created https://github.com/wiringbits/wiringbits-webapp-utils/issues/138 to track this improvement.

AlexITC avatar Nov 08 '22 00:11 AlexITC

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?

nickcoast avatar Nov 16 '22 16:11 nickcoast

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.

AlexITC avatar Nov 16 '22 17:11 AlexITC