Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Scala EE: Bring the Hate

DZone's Guide to

Scala EE: Bring the Hate

Author Roman Nazarenko dives deep into using Scala in the Java EE environment, pulling from his personal experiences.

· Java Zone
Free Resource

Never build auth again! The Okta Developer Platform makes it simple to implement authentication, authorization, MFA and more in Java applications. Get started with the free API.

"Scala EE" or "Probably the fastest way to get hated by both functional programmers and enterprise devs"

Java is old. It's really, really old. In fact, most of today's students did not exist by the time it was released. Still, Java is the most popular enterprise language ever, and Java EE makes it even better providing a centralized way of creating the entire environment your application is intended to be run in. But it's still old. Java works well for kinda old-school people like me—I'm okay with declarations like the following:

public class Task implements Callable<String>, Serializable {
    @Override
    public String call() {
        String result = doSomething();
        return result;
    }
}

If you agree the snippet above is good, then you're probably, well, "old." We're not always old in a physical manner (well, not all of us, at least), but we recall those times we were sitting next to our company's review desk complaining about "those kids who broke it all again." We know verbosity saves the day and implicit mixins are the root of all evil, but everything is moving forward and our industry does as well. In fact, Java EE has figured it out a long time ago: CDI answers dependency injection and events handling, JAX-RS moves focus out of monstrous JSPs, async Servlet 3.0 knocks into JDBC drivers developers' doors and so on.

But, can we go further? Could we just switch the paradigm and move ahead with asynchronous networking, immutable structures, and all that stuff? Not exactly. Our industry has created literally tons of code and we cannot just drop it. Instead of drastic technological breakage, we could just adopt new ideas without breaking our existing workflows. That's what I made while creating my home page, jtalk.me. I was wondering if it's possible to use Scala with my old-n-nice Java EE environment, and yes, it is. Below is a summary of my "Scala EE" development experience. This may be useful if you're going to do Scala smooth and nicely, like I did.

Let's Get to Work!

First of all, let's find out how exactly Scala is different from Java for the VM. Here's a simple Scala class we will use in our comparison. It contains everything one would normally use in a Scala class: properties, custom constructors, weird methods signatures, and so on:

class TestClass @Inject() (private var arg: String, b: String) extends Runnable
                                               with Callable[String]
                                               with Serializable
                                               with StrictLogging {

    private val privateImmutableField = "pif"
    private var privateMutableField = "pmf"

    @Inject
    var publicMutableField = b
    val publicImmutableField = "puif"

    def this() = this("a", "b")

    override def run(): Unit = logger.debug("run()")
    override def call = {
        logger.debug("call()")
        publicImmutableField + privateMutableField
    }
}

object TestClass {
    private val PCONSTANT_VAL = "pcv"
    val CONSTANT_VAL = "pucv"

    def apply(arg: String, b: String) = new TestClass(arg, b)
}

Now, let's make a trick—CFR decompiler comes in handy here. This is what comes out of it (I have replaced logging code with SLF4J-based one as it was too verbose):

@Slf4j
@ScalaSignature(bytes="...")
public class TestClass implements Runnable, Callable<String>, Serializable {

    private String arg;

    private final String publicImmutableField;
    private final String privateImmutableField;

    @Inject
    private String publicMutableField;
    private String privateMutableField;

    public static TestClass apply(String string, String string2) {
        return TestClass$.MODULE$.apply(string, string2);
    }

    public static String CONSTANT_VAL() {
        return TestClass$.MODULE$.CONSTANT_VAL();
    }

    private String arg() { return this.arg; }
    private void arg_$eq(String x$1) { this.arg = x$1; }

    private String privateImmutableField() { return this.privateImmutableField; }
    private String privateMutableField() { return this.privateMutableField; }

    private void privateMutableField_$eq(String x$1) { this.privateMutableField = x$1; }
    public String publicImmutableField() { return this.publicImmutableField; }

    public String publicMutableField() { return this.publicMutableField; }
    public void publicMutableField_$eq(String x$1) { this.publicMutableField = x$1; }

    @Override
    public void run() {
        log.debug("run()");
    }

    @Override
    public String call() {
        log.debug("call()");
        return this.publicImmutableField() + this.privateMutableField();
    }

    @Inject
    public TestClass(String arg, String b) {
        this.arg = arg;
        this.privateImmutableField = "pif";
        this.privateMutableField = "pmf";
        this.publicImmutableField = "puif";
        this.publicMutableField = b;
    }

    public TestClass() {
        this("a", "b");
    }
}

Interesting so far:

  • All the fields are private despite their original visibility. Unimportant as you will merely need public fields in your managed beans.

  • All the public fields have public accessor methods. This is alright as direct fields access is not the way managed beans are intended to be used anyway.

  • Constructors from object declaration hasve been reflected in our class as static methods. This is actually a recent Scala feature for better Scala-Java interoperability, but in our particular case it might be a problem, I'll explain it below.

  • You can put an annotation to the default constructor. Still, it looks weird. We can live with that, I assume.

  • Fields accessors are not JavaBeans-compatible. This is kinda fixable.

  • Fields are kept and their annotations are kept as well. This is great, CDI will be pleased a lot.

About object's methods propagation on class level

CDI and EJB containers have complications with static methods. As those cannot be proxied and may be misleading, they are not expected to be supported by containers. So, we should really avoid using Scala objects in Java EE environment.


JavaBeans requires us to write properties with those get/set methods. Scala uses it's own properties convention, but old-style properties may still be required, for example, in JPA entities. Scala has an easy and convenient way of creating such properties with @BeanProperty annotation:

@BeanProperty
var id: String

This is literally equal to:

private String id;

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

// All the Scala-style properties magic here

Now, once we're all packed and well-prepared, we can just go and write an enterprise application in Scala, right? Well, not really. There are several pitfalls we must take care of before.

JPA Metamodel Generation

I like this fresh, new Criteria API. It's really type-safe and verbose and I use it whenever I can. But what this API would be without a proper metamodel? We need to generate it, and then we need to use it.

You really can skip metamodel (and this whole paragraph) by just applying string names to Root's/Join's getters, but why would you choose to use Criteria API at the first place if you skip all the sweet parts?

The problem with metamodel generation is that it's built upon Java's compile-time annotations processing, which is not really supported by Scala very well. In fact, Scala supports them and scalac will process all the necessary stuff with hibernate-modelgen in project's dependencies, but Scala-to-Java cooperation works poorly there. Scala will not generate metamodel for Java entities used by Scala classes. So, you'll need to either keep all your entities in Java and compile Java first, or keep all your entities in Scala and compile your Java last. I have found no way to break this, unfortunately.

JSF-specific Support

JSF is a weird technology: while being kinda modern and handy, it has it's own pitfalls kept from the very beginning. One of those pitfalls is a weird <ui:repeat> limitations: it ignores all the Iterable<> stuff by only working with standard lists (no Hibernate PersistentBags    ) and arrays. So, Scala's List will not work in any sense. Weird and tricky:

// Nota bene: this import is really required
import scala.collection.JavaConversions._

@Named
@ViewScoped
@ManagedBean
def ArticleBean {

    private var comments: List[Comment] = _

    def getComments() = new java.util.ArrayList(comments)
}

However, all the other JSF parts work well including action methods, view events and all that stuff. This is quite expected from those decompiled samples we've seen above.

CDI Events Support

Except for tricky declarations, everything works as expected:

@ManagedBean
@ApplicationScoped
class EventProcessor {
    def onEvent(@Observes(during = AFTER_SUCCESS) e: MyEvent): Unit = {
        someAction()
    }
}

This "during" thing is exciting. If you've missed it, you should really try it. From now on all the event processors without transactional lifecycle support are just garbage from my point of view.

Container-provided Boundaries

Scala is all about laziness. Lazy initializers, lazy streams, lazy arguments and all those functors you use so easily. But there's a catch. There's always a catch, isn't it? So, how will all that lazy staff work in EJB environment? Hint: not so well.

Consider inspecting the following code:

@Stateless
@LocalBean
class ArticleService {

    @PersistenceContext
    private var em: EntityManager = _

    @TransactionAttribute(REQUIRES_NEW)
    def loadComments(articleIds: List[String]): Stream[Comment] = articleIds.toStream
        .map(em.find(classOf[Article], _))
        .filter(_ != null)
        .flatMap(_.getComments)
        .filter(_ != null)
}

Looks nice, doesn't it? But, what if I tell you about javax.transaction.TransactionRequiredException? Our EntityManager will really walk out of scope very kindly and the entire construct will fail unexpectedly once this stream gets used. Scala is not designed for managed environments and requires extreme care as well as good container knowledge.

You can also loose something out of your secured method this way, but who cares about this method-level security anyway?

Stateful Beans Pitfall

Why do we need stateful session beans? For an extended persistence context, of course. And how do we declare such a context? With type field of our @PersistenceContext annotation, of course. And type is a keyword in Scala...

Well, I can imagine a human being who is interested in slow and non-recyclable POJO stateful bean really is, but why shall we talk about such an unusual demand     at the first place?

However, there's always a workaround, isn't it? And here it comes:

@Stateful
@LocalBean
class ArticleEditor {

    @PersistenceContext(`type` = EXTENDED, synchronization = UNSYNCHRONIZED)
    private var em: EntityManager = _

    @TransactionAttribute(MANDATORY)
    def save() = em.joinTransaction()
}

Val Is Not Final!

While our life was so bright and shiny, here comes the dark part: Scala has no public static final thing at all. All your attempts to create this will, in fact, result in a static method's bytecode instead of a nice field. This could be all right most of the time, but there's a tiny part of Java where method presence will ruin everything. This part is called "annotation argument".

Wouldn't it be nice to write like:

object Constants {
    val FIELD_SIZE = 100
}

@Entity
class SomeEntity {
    @BeanProperty
    @Column(length = Constants.FIELD_SIZE)
    private var field: String = _
}

Well, in fact, this wouldn't work. No chance, end of the road, drop off. Scala will generate public static int FIELD_SIZE() { return 100; } thing here and your annotation will yell at you demanding it's argument to be accessible at compile time. Then what about that:

public class Constants {
    private static final int FIELD_SIZE = 100;
}

This will not work either. The reason is that scalac will not parse your Java class declared as an import. Instead, it will just couple them in a regular way complaining you about this field's non-constant nature anyway. That's why I kept all my model entities in Java nevertheless.

Bonus Chapter

Since you have read that far, here's a simple bonus. Scala has a strange feature: one can declare both field and constructor argument in the same place like:

class SomeClass(private var myField) {
    def doSomethingWithThisField() = ...
}

But, how do I declare an annotation on either field or constructor argument? Looks like Scala designers have not thought of it in time, so we have a huge workaround here:

import scala.annotation.meta.field
import scala.annotation.meta.param

class SomeClass(@(Observes @param)(during = IN_PROGRESS) @(Inject @field) var f: String) {
    def doSomethingWithThisField() = ...
}

Yes, I know observers are pretty useless being placed at constructor's arguments. I was just unable to find a handy annotation with arguments to reflect how weird this workaround is.

This will produce nice bytecode decompiling like:

public class SomeClass {

    @Inject
    public String f;

    public SomeClass(@Observes(during = AFTER_SUCCESS) String f) {
        this.f = f;
    }

    public void doSomethingWithThisField() {
        ...
    }
}

Public fields in managed beans are bad. And single-letter variables are bad either in most cases. But our case matches the exception: this code does not fit into my blog's page otherwise.

As a conclusion: Scala is, well, usable in the Java EE environment. If you're ready for some trade-offs, you can easily use it getting some nice features like extended collections transformers, nice functional programming features, type inference, and so on. However, you should really understand not only what you are writing, but also, how it will work in runtime. If you are looking for a whole new universe but still not ready for a huge step into, you may give "Scala EE" a try.

Build and launch faster with Okta’s user management API. Register today for the free forever developer edition!

Topics:
java ,scala ,java ee

Published at DZone with permission of Roman Nazarenko. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}