skinny-framework / skinny-framework

:monorail: "Scala on Rails" - A full-stack web app framework for rapid development in Scala

Home Page:https://skinny-framework.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Running skinny-blank-app for the first time generates NPE

akirarat opened this issue · comments

I downloaded skinny-blank-app.zip from the hompage, unzipped and ran "skinny run" and generates a NPE when I go to localhost:8080

$ ./skinny run
[info] Loading settings for project global-plugins from idea.sbt ...
[info] Loading global plugins from /Users/xxx/.sbt/1.0/plugins
[info] Loading settings for project skinny-blank-app-build from plugins.sbt ...
[info] Loading project definition from /Users/xxx/git/skinny-blank-app/project
[info] Loading settings for project dev from build.sbt ...
[info] Set current project to skinny-blank-app-dev (in build file:/Users/xxx/git/skinny-blank-app/)
2019-09-12 15:19:28.792:INFO::pool-53-thread-1: Logging initialized @11572ms to org.eclipse.jetty.util.log.StdErrLog
2019-09-12 15:19:28.889:INFO:oejs.Server:pool-53-thread-1: jetty-9.4.16.v20190411; built: 2019-04-11T15:03:03.392Z; git: e0aa4ae4c0fe2e6bb3ecde2eb3e54bc3087b2f52; jvm 1.8.0_201-b09
2019-09-12 15:19:29.016:INFO:oejw.StandardDescriptorProcessor:pool-53-thread-1: NO JSP Support for /, did not find org.eclipse.jetty.jsp.JettyJspServlet
2019-09-12 15:19:29.048:INFO:oejs.session:pool-53-thread-1: DefaultSessionIdManager workerName=node0
2019-09-12 15:19:29.048:INFO:oejs.session:pool-53-thread-1: No SessionScavenger set, using defaults
2019-09-12 15:19:29.049:INFO:oejs.session:pool-53-thread-1: node0 Scavenging every 600000ms
2019-09-12 15:19:29.176  INFO   --- [ool-53-thread-1] skinny.micro.SkinnyListener              : The cycle class name from the config: Bootstrap
2019-09-12 15:19:29.384 DEBUG   --- [ool-53-thread-1] skinny.micro.SkinnyListener              : Loaded lifecycle class: class Bootstrap
2019-09-12 15:19:29.457  INFO   --- [ool-53-thread-1] skinny.micro.SkinnyListener              : Initializing life cycle class: Bootstrap
2019-09-12 15:19:29.713 DEBUG   --- [ool-53-thread-1] scalikejdbc.ConnectionPool$              : Registered connection pool : ConnectionPool(url:jdbc:h2:file:./db/development;MODE=PostgreSQL;AUTO_SERVER=TRUE, user:sa) using factory : <default>
2019-09-12 15:19:30.313:INFO:oejsh.ContextHandler:pool-53-thread-1: Started o.e.j.w.WebAppContext@75f13c3b{/,[file:///Users/xxx/git/skinny-blank-app/src/main/webapp/],AVAILABLE}
2019-09-12 15:19:30.496:INFO:oejs.AbstractConnector:pool-53-thread-1: Started ServerConnector@39be3791{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
2019-09-12 15:19:30.497:INFO:oejs.Server:pool-53-thread-1: Started @13277ms
[success] Total time: 2 s, completed Sep 12, 2019 3:19:30 PM
1. Waiting for source changes in project dev... (press enter to interrupt)
2019-09-12 15:19:32.418 ERROR   --- [p1804134626-390] controller.Controllers$root$             : <null>

java.lang.NullPointerException: null
	at java.io.File.<init>(File.java:277)
	at skinny.controller.feature.ScalateTemplateEngineFeature.generateWelcomePageIfAbsent(ScalateTemplateEngineFeature.scala:150)
	at skinny.controller.feature.ScalateTemplateEngineFeature.generateWelcomePageIfAbsent$(ScalateTemplateEngineFeature.scala:145)
	at skinny.controller.SkinnyController.generateWelcomePageIfAbsent(SkinnyController.scala:8)
	at skinny.controller.feature.ScalateTemplateEngineFeature.templateExists(ScalateTemplateEngineFeature.scala:126)
	at skinny.controller.feature.ScalateTemplateEngineFeature.templateExists$(ScalateTemplateEngineFeature.scala:123)
	at skinny.controller.SkinnyController.templateExists(SkinnyController.scala:8)
	at skinny.controller.feature.TemplateEngineFeature.render(TemplateEngineFeature.scala:29)
	at skinny.controller.feature.TemplateEngineFeature.render$(TemplateEngineFeature.scala:26)
	at skinny.controller.SkinnyController.render(SkinnyController.scala:8)
	at controller.RootController.index(RootController.scala:7)
	at controller.Controllers$root$.$anonfun$indexUrl$1(Controllers.scala:14)
	at skinny.micro.SkinnyMicroBase.liftAction(SkinnyMicroBase.scala:300)
	at skinny.micro.SkinnyMicroBase.liftAction$(SkinnyMicroBase.scala:298)
	at skinny.controller.SkinnyController.liftAction(SkinnyController.scala:8)
	at skinny.micro.SkinnyMicroBase.$anonfun$invoke$1(SkinnyMicroBase.scala:294)
	at skinny.micro.ApiFormats.withRouteMultiParams(ApiFormats.scala:196)
	at skinny.micro.ApiFormats.withRouteMultiParams$(ApiFormats.scala:182)
	at skinny.controller.SkinnyController.withRouteMultiParams(SkinnyController.scala:8)
	at skinny.micro.SkinnyMicroBase.invoke(SkinnyMicroBase.scala:294)
	at skinny.micro.SkinnyMicroBase.invoke$(SkinnyMicroBase.scala:292)
	at skinny.controller.SkinnyController.invoke(SkinnyController.scala:8)
	at skinny.micro.SkinnyMicroBase.$anonfun$executeRoutes$15(SkinnyMicroBase.scala:208)
	at scala.collection.immutable.Stream.flatMap(Stream.scala:489)
	at skinny.micro.SkinnyMicroBase.runActions$1(SkinnyMicroBase.scala:208)
	at skinny.micro.SkinnyMicroBase.$anonfun$executeRoutes$16(SkinnyMicroBase.scala:241)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at skinny.micro.SkinnyMicroBase.cradleHalt$1(SkinnyMicroBase.scala:215)
	at skinny.micro.SkinnyMicroBase.executeRoutes(SkinnyMicroBase.scala:239)
	at skinny.micro.SkinnyMicroBase.executeRoutes$(SkinnyMicroBase.scala:135)
	at skinny.controller.SkinnyController.executeRoutes(SkinnyController.scala:8)
	at skinny.micro.SkinnyMicroBase.$anonfun$handle$1(SkinnyMicroBase.scala:110)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at scala.util.DynamicVariable.withValue(DynamicVariable.scala:62)
	at skinny.micro.base.SkinnyContextInitializer.withResponse(SkinnyContextInitializer.scala:98)
	at skinny.micro.base.SkinnyContextInitializer.withResponse$(SkinnyContextInitializer.scala:96)
	at skinny.controller.SkinnyController.withResponse(SkinnyController.scala:8)
	at skinny.micro.base.SkinnyContextInitializer.$anonfun$withRequestResponse$1(SkinnyContextInitializer.scala:78)
	at scala.util.DynamicVariable.withValue(DynamicVariable.scala:62)
	at skinny.micro.base.SkinnyContextInitializer.withRequest(SkinnyContextInitializer.scala:89)
	at skinny.micro.base.SkinnyContextInitializer.withRequest$(SkinnyContextInitializer.scala:87)
	at skinny.controller.SkinnyController.withRequest(SkinnyController.scala:8)
	at skinny.micro.base.SkinnyContextInitializer.withRequestResponse(SkinnyContextInitializer.scala:77)
	at skinny.micro.base.SkinnyContextInitializer.withRequestResponse$(SkinnyContextInitializer.scala:75)
	at skinny.controller.SkinnyController.withRequestResponse(SkinnyController.scala:8)
	at skinny.micro.SkinnyMicroBase.handle(SkinnyMicroBase.scala:110)
	at skinny.micro.SkinnyMicroBase.handle$(SkinnyMicroBase.scala:105)
	at skinny.controller.SkinnyController.skinny$micro$contrib$FlashMapSupport$$super$handle(SkinnyController.scala:8)
	at skinny.micro.contrib.FlashMapSupport.$anonfun$handle$1(FlashMapSupport.scala:66)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at scala.util.DynamicVariable.withValue(DynamicVariable.scala:62)
	at skinny.micro.base.SkinnyContextInitializer.withRequest(SkinnyContextInitializer.scala:89)
	at skinny.micro.base.SkinnyContextInitializer.withRequest$(SkinnyContextInitializer.scala:87)
	at skinny.controller.SkinnyController.withRequest(SkinnyController.scala:8)
	at skinny.micro.contrib.FlashMapSupport.handle(FlashMapSupport.scala:39)
	at skinny.micro.contrib.FlashMapSupport.handle$(FlashMapSupport.scala:38)
	at skinny.controller.SkinnyController.skinny$micro$contrib$ScalateSupport$$super$handle(SkinnyController.scala:8)
	at skinny.micro.contrib.ScalateSupport.handle(ScalateSupport.scala:132)
	at skinny.micro.contrib.ScalateSupport.handle$(ScalateSupport.scala:131)
	at skinny.controller.SkinnyController.handle(SkinnyController.scala:8)
	at skinny.micro.SkinnyMicroFilterBase.$anonfun$doFilter$1(SkinnyMicroFilterBase.scala:29)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at scala.util.DynamicVariable.withValue(DynamicVariable.scala:62)
	at skinny.micro.SkinnyMicroFilterBase.doFilter(SkinnyMicroFilterBase.scala:29)
	at skinny.micro.SkinnyMicroFilterBase.doFilter$(SkinnyMicroFilterBase.scala:20)
	at skinny.controller.SkinnyController.doFilter(SkinnyController.scala:8)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1610)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:540)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1700)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1345)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:480)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1667)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1247)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)
	at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:220)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
	at org.eclipse.jetty.server.Server.handle(Server.java:505)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:370)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:267)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
	at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:786)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:743)
	at java.lang.Thread.run(Thread.java:748)

Using Java 1.8.0_201-b09
I also tried the instructions from the homepage.

/skinny g scaffold members member name:String activated:Boolean luckyNumber:Option[Long] birthday:Option[LocalDate]
./skinny db:migrate
./skinny run

Then hit http://localhost:8080/members

Same NPE stacktrace.

java.lang.NullPointerException: null
at java.io.File.(File.java:277)
at skinny.controller.feature.ScalateTemplateEngineFeature.generateWelcomePageIfAbsent(ScalateTemplateEngineFeature.scala:150)
.. and so forth.

hmm, I'm still not sure about how to reproduce your problem yet.

This is a MacOS Catalina (10.15) specific bug.

All user files and directories are mounted under /System/Volumes/Data and MacOS mounted in a read-only partition, not accessible to users.

The index.html.ssp file now has an alias (symbolic link). For example, the resource path:

   /WEB-INF/views/root/index.html.ssp

Will resolve to this file path:

   file:///Users/xxx/git/siptea/src/main/webapp/WEB-INF/views/root/index.html.ssp

And also have an alias path:

   /System/Volumes/Data/Users/xxx/git/siptea/src/main/webapp/WEB-INF/views/root/index.html.ssp

Inside org.eclipse.jetty.server.handler.ContextHandler#checkAlias, it will return true for resource.isAlias() but later fail in org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker#check because hasSymbolicLink(path) will return false, when it should return true.

A simple unit test finds the problem. Files.isSymbolicLink(path1) should return true but it isn't, causing the caller, hasSymbolicLink(), to return false. I'm not sure how to fix the problem yet. I'll try upgrading to the newest Catalina and try again.

class FilesSameTest extends FlatSpec {
  "2 files" should "be the same" in {
    val path1 = Paths.get("/Users/xxx/git/siptea/src/main/webapp/WEB-INF/views/root/index.html.ssp")
    val path2 = Paths.get("/System/Volumes/Data/Users/xxx/git/siptea/src/main/webapp/WEB-INF/views/root/index.html.ssp")

    println(hasSymbolicLink(path1)) // returns false
    println(Files.isSameFile(path1, path2)) // returns true
    println(Files.isSymbolicLink(path1)) // returns false
  }

  // Copy pasted from AllowSymLinkAliasChecker#hasSymbolicLink
  private def hasSymbolicLink(path: Path): Boolean = { // Is file itself a symlink?
    if (Files.isSymbolicLink(path)) return true
    // Lets try each path segment
    var base = path.getRoot
    import scala.collection.JavaConversions._
    for (segment <- path) {
      base = base.resolve(segment)
      if (Files.isSymbolicLink(base)) return true
    }
    false
  }

I upgraded to the newest Catalina and path1.toRealPath() returns the "/User/home..." string instead of "/System/Volumes/Data..." string. Seems like this is fixed.