Perhaps you’ve thought about integrating Scala (or another JVM language) with your hybris project? As it turns out, this is not as difficult as it may at first seem. I will demonstrate a sample integration with Scala (the original implementation was based on hybris 4.7 – but as you will see, this is equally valid for 5.0+). At runtime, you typically only require that the Scala jar is present on the classpath (putting it into your extension’s lib directory). So let’s explore how this would work.
The first thing you will need is a Scala distribution. For this example, I am using 2.10. A search will turn up plenty of options for installing. Starting at Typesafe would be a good bet or simply visiting Scala language home will also give several options for your platform of choice. Assuming you have a Scala distribution setup and ready, you could be able to execute ‘scalac’ to compile Scala source or simply execute ‘scala’ to get a repl prompt (read evaluate print loop) which is extremely useful for exploring Scala.
Next, let us examine where to put the source. For this we will generate a normal hybris extension and simply include a Scala compile task and source folder for it to compile. I will perform this using hybris 5.0.0 to backup my claim that the 4.7 integration also works properly in 5.0+. For this I chose the name ‘scalamodule’ for an extension based on yempty. As with any extension, add scalamodule to your localextensions.xml.
Now, that we have our new scalamodule extension, we need to add some Scala source, copy the scala-library.jar and setup the compile task.
From ${HYBRIS_BIN_DIR}/custom/scalamodule (adjust as appropriate for your Scala install location):
cp /usr/local/Cellar/scala/2.10.1/libexec/lib/scala-library.jar lib/.
mkdir scalasrc
Now, in buildcallbacks.xml:
<project name="scalamodule_buildcallbacks">
<property name="scala.home" value="/usr/local/Cellar/scala/2.10.1"/>
<property name="scala.compiler" value="${scala.home}/libexec/lib/scala-compiler.jar"/>
<property name="scala.reflect" value="${scala.home}/libexec/lib/scala-reflect.jar"/>
<property name="scala.library" value="${scala.home}/libexec/lib/scala-library.jar"/>
<property name="scala.lib" value="${scala.home}/libexec/lib"/>
<taskdef resource="scala/tools/ant/antlib.xml">
<classpath>
<pathelement location="${scala.compiler}"/>
<pathelement location="${scala.reflect}"/>
<pathelement location="${scala.library}"/>
</classpath>
</taskdef>
<macrodef name="scalamodule_after_compile_core">
<sequential>
<scalac
srcdir="${ext.scalamodule.path}/scalasrc"
destdir="${ext.scalamodule.path}/classes" classpath="${build.classpath}:${scala.reflect}:${scala.compiler}:${scala.library}">
<include name="**/*.scala"/>
</scalac>
</sequential>
</macrodef>
</project>
The interesting bits from above:
- Scala source is compiled to the same location as the extension’s Java source.
- Scala source is compiled after the Java source. Which means your Scala can depend on the Java classes from that extension.
- The scalac task is distributed with the Scala compiler jar (notice the taskdef).
Things would not be terribly interesting unless we created a Scala bean in the Spring context. Once again, from the extension’s root:
mkdir -p scalasrc/de/hybris/scalasample
Then, create a very simple Scala class in that location (SimpleBean.scala – although unlike Java, Scala is flexible in what you call your source files – e.g. we could have called it xyz.scala):
package de.hybris.scalasample
class SimpleBean {
def init() = println(“Simple scala bean up and running”)
}
and declare it in that module’s Spring config:
<bean id="scalabean" class="de.hybris.scalasample.SimpleBean" init-method="init"/>
Running ‘ant’, you should see some output from the build indicating the scalac task was executed (if you don’t, try touching the the extension directory to ensure the compile is triggered and running ‘ant’ again)
[scalac] Compiling 1 source file to /Users/sams/Documents/hy/releases/5.0/5.0.0/c/experiment/hybris/bin/custom/scalamodule/classes
(You will likely see some other warnings about scalac not seeing classes where it expects in the platform – from my experience these can be safely ignored – e.g. [scalac] Element '/Users/sams/Documents/hy/releases/5.0/5.0.0/c/experiment/hybris/bin/platform/ext/core/classes' does not exist.
That’s it, you’re now compiling and running Scala beans within hybris! After launching hybris, you should see a message in the console indicating that the Scala bean is up and running:
grep "Simple scala" ../../../log/tomcat/console-20130526.log
INFO | jvm 1 | main | 2013/05/26 13:36:37.959 | Simple scala bean up and running
In the interest of keeping this post of reasonable length I won’t go into tooling, but I’d recommend taking a look at the Scala IDE. For mixed mode projects, I found it easiest to create a separate Scala Eclipse project to manage the Scala source and simply link the scalasrc folder from the hybris extension. Some tinkering with the project builders would likely obviate the extra Eclipse project to manage the Scala source/compiles from within the IDE.
Finally, I leave you with a few code snippets from my hybris 4.7 experiment that was the account controller rewritten in Scala. This will demonstrate how to use a few of the Spring MVC annotations with Scala. If you have any questions on Scala integration or this post, please feel free to contact me.
// imports elided - they look essentially the same as the corresponding Java imports
@Controller
@Scope("tenant")
@RequestMapping(Array("/new-account"))
class AccountPageControllerScala extends AbstractSearchPageController { // painlessly extending a Java class
val LOG = Logger.getLogger(this.getClass)
val REDIRECT_MY_ACCOUNT = REDIRECT_PREFIX + “/my-account”
val REDIRECT_TO_ADDRESS_BOOK_PAGE = REDIRECT_PREFIX + “/my-account/address-book”
// CMS Pages
val ACCOUNT_CMS_PAGE = “account”
val PROFILE_CMS_PAGE = “profile”
// some constants elided
@Resource(name = “accountBreadcrumbBuilder”)
var accountBreadcrumbBuilder: ResourceBreadcrumbBuilder = _
@Resource(name = “orderFacade”)
var orderFacade: OrderFacade = _
// other dependencies elided…
@RequestMapping(method = Array(RequestMethod.GET))
def account(model: Model) = {
storeCmsPageInModel(model, getContentPageForLabelOrId(ACCOUNT_CMS_PAGE))
setUpMetaDataForContentPage(model, getContentPageForLabelOrId(ACCOUNT_CMS_PAGE))
model.addAttribute(“breadcrumbs”, accountBreadcrumbBuilder.getBreadcrumbs(null))
model.addAttribute(“metaRobots”, “no-index,no-follow”)
ControllerConstants.Views.Pages.Account.AccountHomePage
}
// shows using @RequestParam
@RequestMapping(value = Array(“/orders”), method = Array(RequestMethod.GET))
def orders(@RequestParam(value = “page”, defaultValue = “0”) page: Int, @RequestParam(value = “show”, defaultValue = “Page”) showMode: AbstractSearchPageController.ShowMode,
@RequestParam(value = “sort”, required = false) sortCode: String, model: Model): String =
{ /* implementation elided */ }
// example of using a little Scala syntax
@RequestMapping(value = Array(“/profile”), method = Array(RequestMethod.GET))
def profile(model: Model): String =
{
val customerData = customerFacade.getCurrentCustomer
val titles = userFacade.getTitles
if (customerData.getTitleCode != null) {
for (title <- titles.find(_.getCode == customerData.getTitleCode)) {
model.addAttribute(“title”, title)
}
}
// remainder elided
}
[…] recommend checking out Scala Integration on the hybris tech blog written by Sam Schneider to see how straight forward it can be to […]
I thought my Scala coding days were over when we moved to Hybris. Not anymore. Thanks very much for exploring this.
I’m glad you enjoyed it. I hope I have motivated you to at least try out Scala for some part of your project. It sounds like you would not require any convincing on the value proposition around Scala!
Hi Sam, I found the Java first, then Scala compilation interesting. What if you wanted to add some Scala code and depend on it in another extension. For groovy, there is groovyc which takes care of this cross-compilation issue. This must be solved for Scala, too, somehow I think. do you know?
The bit around being able to depend on Java in the _same_ extension was merely to note the direction of the dependency I chose (if you like, you could reverse the dependency direction… it’s those circular things we try to avoid!) Regarding being dependent on anything else in the platform, there is no issue there either. For instance, I built out an account controller which depended on several extensions (btg and acceleratorfacades in this case). During the build process, the entire classpath is available via the build.classpath variable (see the scalac task above, for example). If it’s a circular dependency that you really would like, I’d have to tinker with that to see how it would work out.
Thanks for this write-up, Sam. This answers one of the questions I had from earlier this afternoon.
My pleasure. Please feel free to reach out with any future questions. Judging from the level of interest, there will be more installments of Scala here.
Nice work! I love scala and thanks for publishing this post. I tried accessing some of the service classes from platform and it was giving me error like “[scalac] e error while loading xxxxxFacade, class file ‘xxxxxFacade.class’ is broken. Have you faced this issue ?
Thanks for the comment and question. I haven’t seen this before. It sounds a bit a like this issue https://issues.scala-lang.org/browse/SI-7455 which is still open at this time. If you can give the exact class that is giving you the trouble, I can try to reproduce it.