[2.3.x] Sbt Web Assets - Packaging problem for multi-project

332 views
Skip to first unread message

chungonn

unread,
Oct 23, 2014, 12:13:41 AM10/23/14
to play-fr...@googlegroups.com
I have a multi-project where Project A depends on Project B and both Project A and Project B are Play 2 apps.

Project B is a full-fledged app with its own Models, Views, Controllers and routes. Project B is also a base app which provides common Models, Views and Controllers to other apps like  Project A as mentioned earlier.

Here are my problems I encountered: - 

1. Assets dependency for a multi-project is different for a multi-source project as compared to a multi-jar project .

In a multi-source project scenario, all the assets are placed directly in the public folder i.e. both Project A's and Project B's assets are found in /public however in a multi-jar project scenario, the assets of jar project (B) are placed in /public/B/ folder. This poses a big challenge when the base project's (B) source is packaged separately, all assets in Project B accessing from Project A must change from /public/file.css to /public/lib/b/file.css. Sbt-Web will take Project B as a web jar the moment project B is packaged using publish. What is the recommended approach for developing from  a multi-source project development  and over time as the base project matures evolve into a multi jars dependency? Any insight on this will be appreciated.

2. Jars from  Stage packaging is different from Publish
I only realized the significance in packaging using stage as compared to publish after I convert my base project from source-dependent to jar.  In 'stage' the project assets are place in a separate jar which are in the /public folder, whereas in 'publish' the project assets are place as a web-jars/public/lib/project-name folder. How to I get 'publish' to  emit an assets jar the same way stage?

I am using Sbt-Web version 1.1.1 and sbt-less version 1.0.4.

Any help will be greatly appreciated

Peter Vlugter

unread,
Oct 23, 2014, 1:21:27 AM10/23/14
to play-framework
Hi,

Yes, sbt-web 1.1 has multi-project support that is designed to work for both source and binary dependencies.

Play 2.3, however, also has some built-in multi-project aggregation of assets, because sbt-web 1.0 didn't yet have multi-project support.

So using Play 2.3.5 with sbt-web 1.1.1 you should actually see the assets from project B in two places: /assets/b.css (from Play's aggregation) and also /assets/lib/b/b.css (from sbt-web's webjar-style import). You should just use the `lib/b` one, and this will work for both source dependencies and published jar dependencies.

You can also disable the asset aggregation with this sbt setting:

PlayKeys.playAggregateAssets := false

Play 2.4 will simply use the multi-project support in sbt-web 1.1.

Cheers,
Peter

Peter Vlugter

unread,
Oct 23, 2014, 1:43:29 AM10/23/14
to play-framework
One extra thing to note: if you're using assets from project B in common views, then these are referred to differently in projects A and B. This is the awkward part of treating asset dependencies as webjars. The benefit is that the lib/module prefix automatically namespaces the assets, but the downside is that templates in project B have the wrong asset paths in project A.

If this is the case then you can have the assets from project B imported without the lib/b prefix by using this setting:

WebKeys.directWebModules in Assets += "b"

Replacing "b" with the appropriate module name. This should also work for both source and binary dependencies.

- Peter

chungonn

unread,
Oct 23, 2014, 2:39:36 AM10/23/14
to play-fr...@googlegroups.com
Hi Peter,

Thanks! It now works for assets but not the routes...


On Thursday, 23 October 2014 13:43:29 UTC+8, Peter Vlugter wrote:
One extra thing to note: if you're using assets from project B in common views, then these are referred to differently in projects A and B. This is the awkward part of treating asset dependencies as webjars. The benefit is that the lib/module prefix automatically namespaces the assets, but the downside is that templates in project B have the wrong asset paths in project A.

This is exactly the situation I am in. Your resolution below helped to solved the problem. However, there is still a glitch that needs to be resolved. 

With the 'directWebModules' the assets from Project B are now in the public, which is what I need, however, then the I click on an <a>  tag, I received a RuntimeException: java.lang.NoSuchFieldError: MBRbacController. This controller is in available in the app (Project A). I suspect the problem is due to Project A is using Project B's route and this is confirmed when I staged Project A and ran it and it works as expected. As a staged application orders the jars based on their dependencies.

2014-10-23 14:20:02,778 - [info] play - Application started (Dev)
2014-10-23 14:20:09,231 - [error] application - 

! @6k1bf8hn2 - Internal server error, for (GET) [/space/1/edit] ->

play.api.Application$$anon$1: Execution exception[[RuntimeException: java.lang.NoSuchFieldError: MBRbacController]]
at play.api.Application$class.handleError(Application.scala:296) ~[play_2.11-2.3.5.jar:2.3.5]
at play.api.DefaultApplication.handleError(Application.scala:402) [play_2.11-2.3.5.jar:2.3.5]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$14$$anonfun$apply$1.applyOrElse(PlayDefaultUpstreamHandler.scala:205) [play_2.11-2.3.5.jar:2.3.5]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$14$$anonfun$apply$1.applyOrElse(PlayDefaultUpstreamHandler.scala:202) [play_2.11-2.3.5.jar:2.3.5]
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36) [scala-library-2.11.2.jar:na]
Caused by: java.lang.RuntimeException: java.lang.NoSuchFieldError: MBRbacController
at play.api.mvc.ActionBuilder$$anon$1.apply(Action.scala:523) ~[play_2.11-2.3.5.jar:2.3.5]
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:130) ~[play_2.11-2.3.5.jar:2.3.5]
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:130) ~[play_2.11-2.3.5.jar:2.3.5]
at play.utils.Threads$.withContextClassLoader(Threads.scala:21) ~[play_2.11-2.3.5.jar:2.3.5]
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:129) ~[play_2.11-2.3.5.jar:2.3.5]
Caused by: java.lang.NoSuchFieldError: MBRbacController
at views.html.tags.spaceMultiValueTags$.apply(spaceMultiValueTags.template.scala:35) ~[classes/:1.0-SNAPSHOT]
at views.html.spaceDetail$.apply(spaceDetail.template.scala:225) ~[classes/:na]
at controllers.MBRbacController$$anonfun$showSpaceWithConfig$1$$anonfun$apply$5$$anonfun$apply$6$$anonfun$apply$7$$anonfun$5.apply(MBRbacController.scala:71) ~[classes/:na]
at controllers.MBRbacController$$anonfun$showSpaceWithConfig$1$$anonfun$apply$5$$anonfun$apply$6$$anonfun$apply$7

Is there a work around for my problem?

Once again, thank you for a speedy and detailted response.

Regards
chungonn 


Peter Vlugter

unread,
Oct 23, 2014, 3:07:30 AM10/23/14
to play-framework

On 23/10/2014, at 7:39 pm, chungonn <chun...@gmail.com> wrote:

> Thanks! It now works for assets but not the routes...
>
> On Thursday, 23 October 2014 13:43:29 UTC+8, Peter Vlugter wrote:
>> One extra thing to note: if you're using assets from project B in common views, then these are referred to differently in projects A and B. This is the awkward part of treating asset dependencies as webjars. The benefit is that the lib/module prefix automatically namespaces the assets, but the downside is that templates in project B have the wrong asset paths in project A.
>
> This is exactly the situation I am in. Your resolution below helped to solved the problem.

Cool, good to hear.

> However, there is still a glitch that needs to be resolved.
>
> With the 'directWebModules' the assets from Project B are now in the public, which is what I need, however, then the I click on an <a> tag, I received a RuntimeException: java.lang.NoSuchFieldError: MBRbacController. This controller is in available in the app (Project A). I suspect the problem is due to Project A is using Project B's route and this is confirmed when I staged Project A and ran it and it works as expected. As a staged application orders the jars based on their dependencies.

Have you read through the documentation on multiple projects?

https://playframework.com/documentation/2.3.x/SBTSubProjects

There's information there about using routes across projects. If that doesn't help, could you create a sample project that demonstrates the issue?

Cheers,
Peter

chungonn

unread,
Oct 23, 2014, 4:48:22 AM10/23/14
to play-fr...@googlegroups.com
Hi Peter,



Have you read through the documentation on multiple projects?

https://playframework.com/documentation/2.3.x/SBTSubProjects

Yes, i have read it sometime back. And I have read it again and it has 2 new Play Key settings I have discovered. I applied both but it still didnt work. 

PlayKeys.generateRefReverseRouter := false //In Project B.

PlayKeys.devSettings += ("application.router", "a.Routes")  //In Project A, I used name a.routes instead of routes


There's information there about using routes across projects. If that doesn't help, could you create a sample project that demonstrates the issue?

I will try to reproduce the issue in a sample projects.
 
Thanks once again


Regards
chungonn

chungonn

unread,
Oct 23, 2014, 7:58:42 AM10/23/14
to play-fr...@googlegroups.com
Hi Peter,

I am able to reproduce a situation similar to project described earlier.

In the attached file, I have 2 projects Project A and Project B and Project A is dependent on Project B.Please note that both projects are at the same level and not nested.

Project A build.sbt is configured as binary dependency. It can be set to source dependency by uncommenting and commenting out relevant lines. 

Case A -  'Stage' Project A - to see the expected output 
1. Publish Local for Project B
2. Stage Project A 
3. Run Project A from  stage/bin 
4  Access the url localhost:9000/hi. The response is shown below, it has a self link 

Hello World from Extend Self Link

----------------------------

Case B - Run Project A within Play console i.e. in dev mode
1. Remove the Project A target folder
2. Run Project in Play console 
3 Access the url  localhost:9000/hi. The response is different from the stage version, the self link is missing

Hello World from Extend

Any changes made to hi.scala.html has no effect. It seems it is using Project B's hi.scala.html.

-----------------------------

If we would be use source dependency, the output is consistent to case A which is expected

Please let me know if you need further clarification.

Regards
chungonn
mult-projects.tar.gz

Peter Vlugter

unread,
Oct 23, 2014, 3:53:29 PM10/23/14
to play-framework
Hi chungonn,

So you have two classes at the same location, views.html.hi, one from each project. You shouldn't rely on classpath ordering to select one of these but instead either give them different names or put them under different packages. Using different packages is the normal approach for multi-project apps. The same goes for controllers, views, and models. All classes should have unique fully qualified names.

So for multi-module projects you need to shift from the convention in Play of using the top-level 'controllers' and 'views' packages to using a module specific package, like 'b.controllers' or 'controllers.b'. This is also described in the documentation:

https://playframework.com/documentation/2.3.x/SBTSubProjects#Splitting-your-web-application-into-several-parts

Cheers,
Peter
> --
> You received this message because you are subscribed to the Google Groups "play-framework" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
> <mult-projects.tar.gz>

chungonn

unread,
Oct 23, 2014, 8:50:48 PM10/23/14
to play-fr...@googlegroups.com
Hi Peter,


On Friday, 24 October 2014 03:53:29 UTC+8, Peter Vlugter wrote:
Hi chungonn,

So you have two classes at the same location, views.html.hi, one from each project. You shouldn't rely on classpath ordering to select one of these but instead either give them different names or put them under different packages. Using different packages is the normal approach for multi-project apps. The same goes for controllers, views, and models. All classes should have unique fully qualified names.
Classpath ordering is a natural thing in Java. I believe that in Play 2.2.x the classpath ordering for a stage/dist app is enforced before that it wasn't.  Also the classpath ordering is respected in both source dependency and jar dependency but not in a mixed dependency as in my case. The link you provided is ambiguous about the type of multi project dependency. 

The dependency project A using the same view  a.views.hi.scala.html  is because it is extending the ProjectB's view. I apologize for not able to reproduce the problem I reported earlier. Now I have reproduced the same problem I am encountering. In the attached source, you will notice that I am not using the same view in both projects A and B. The problem this time is Project A's is not accessible due to the classpath issue.


Nevertheless, thanks for helping to looking into the matter. Greatly appreciate it.

Regards
chungonn 
multi-projects2.tar.gz

chungonn

unread,
Oct 24, 2014, 4:49:01 AM10/24/14
to play-fr...@googlegroups.com
Hi Peter,

I have file a bug report here - https://github.com/playframework/playframework/issues/3553

Regards
chungonn
Reply all
Reply to author
Forward
0 new messages