Enterprise-less Life

in Jvm World

Yehor Bondar

About Me

  • Senior Software Engineer
  • Over 5 years in JVM world
  • Java/Scala/Groovy developer
  • Bunch of JVM frameworks user

Yehor Bondar

key Notes

  • What is an enterprise?
  • Java lang verbosity pitfalls and ways to resolve it
  • JavaEE: Specifications, standards and a lot of implementations
  • Frameworks battle to get the currency: JavaEE oriented vs Enteprise-less world
  • Conclusions

Enterprise?

Slowness of software is a complex aspect:

  • Slower coding
  • Slower decision making
  • Slower deployments
  • Slower support 

Ways to exit

  • Reduce code verbosity
  • Do not over engineering things (KISS) 
  • Use proper frameworks
  • Do we really need monolith?

Is Code verbosity good?

The important point about verbosity is not the time you spend typing: it’s about how difficult it is to read and understand the code.

 

VS


And verbosity makes code easier to read. You don’t have to know anything to understand the code. Everything you need to know is there in front of you.

java verbosity pitfalls

The verbosity of Java code also encourages developers to implement more abstractions and reusable components. It’s easier to just write more code than to stop and think about reusable solutions. In Java and other verbose languages, ‘coding for the moment’ is a losing strategy.


Well known patterns:

  • Abstract Factory & Factory Method;
  • Strategy & Facade.

java verbosity pitfalls

  • Getters & Setters Hell;
  • Checked exceptions;
  • Absence of named method parameters and default values;
  • No pattern matching
  • No closures and lambdas (before Java 8);
  • Functional paradigm is weak (type classes, type aliases, variance, etc.)
  • Etc.?

GEtters & Setters

Project Lombok

public class Person {
  @Getter @Setter private int age = 10;

  @Setter(AccessLevel.PROTECTED) 
  private String name;

  @Override public String toString() {
  return String.format("%s (age: %d)", name, age);
 }
}
case class Person(age: Int, name: String)

class Person(var age: Int, var name: String)

val person = Person(10, "Bob")
person.age = 25

Scala

Checked exceptions

public class Test {
    // No throws clause here
    public static void main(String[] args) {
        doThrow(new SQLException());
    }
    static void doThrow(Exception e) {
        Test.<RuntimeException> doThrow0(e);
    }
    @SuppressWarnings("unchecked")
    static <E extends Exception> void doThrow0(Exception e) throws E {
        throw (E) e;
    }
}

Checked exceptions

Scala

val result = Try {
 throw new SQLException("DB is failed") 
}

if(result.isSuccess) ???
else ???

result foreach { db =>
 //execute only if DB is OK
}

MEthod parameters

Scala

def test(age: Int, name: String = "exampleText"): Unit = {

}

test(10)
test(10, "realName")
test(age = 10, name = "goodName")
public void test(int age) {
    test(age, "exampleText");
}

public void test(int age, String name) {

}

Pattern Matching

trait Person {
  def age: Int
  def name: String
}
case class Employee(department: String, age: Int, name: String) extends Person
case class Student(university: String, age: Int, name : String) extends Person

val person: Person = Student("KNURE", 30, "Bob")

person match {
  case Employee(_, 30, "Bob") => println("Correct man")
  case Employee(_, 25, "July") => println("Correct woman")
  case Student("KNURE", _, _) => println("Student from KNURE")
  case _ => println("somebody else")
}

Lambdas and collections

List<Student> students = persons.stream()
        .filter(p -> p.getAge() > 18)
        .map(Student::new)
        .collect(Collectors.toList());

If you have Java 8

If not

val students = persons.collect {case p if p.age > 18 => new Student(p)}
def students = persons.findAll { person ->
    person.age > 18
}.collect { person ->
    new Student(person)
}

Groovy Collections API

Solution?

  • Avoid Java and use another JVM language
  • Include library into your existing project and start refactoring
    • Include necessary compiler plugin
    • Include library
<dependency>
    <groupId>org.scala-lang</groupId>
    <artifactId>scala-library</artifactId>
    <version>2.12.1</version>
</dependency>
<plugin>
  <groupId>org.scala-tools</groupId>
  <artifactId>maven-scala-plugin</artifactId>
  <executions>
   <execution>
    <goals>
     <goal>compile</goal>
     <goal>testCompile</goal>
    </goals>
   </execution>
  </executions>
</plugin>

TEst Project Overview

  • REST-API service
  • REST client
  • Simple DB iteration
  • Unit and Integration tests

Implementations

J2EE

 

Play 2 Framework

Metrics to measure

  • WPP - WTF Per Project
  • GPP - Googles Per Project
  • Amount of configuration files
  • Simplicity

What is not covered

  • This is simple project - NOT Production monster
  • No perfomance benchmarks - keep it easy

Java Enterprise Edition

XML configurable

EJB (ejb-jar.xml) JAX-WS (sun-jaxws.xml)
JPA (persistence.xml) JAX-RS (web.xml)
Servlet-Api/JSP (web.xml) JMS(*-jms.xml)
JSF (faces-config.xml) CDI (beans.xml)

J2EE disadvantages

  • XML-hell
  • Heavy-weight Application Servers
  • Dozen of layers
  • Impossible to test outside container

Java EE 7 & Ejb 3.x Improvements

  • Annotation based (can be done without ejb-jar.xml)
  • DI versus JNDI (@EJB)
  • EJB Lite (allows to use EJB features but withing WAR files)
  • Increased portability (JNDI names )
  • Fully compliment Java EE 7 (optional EJB 2.0)
  • JMS 2.0

J2EE in action

  • EJB 3.1
  • JPA 2.0 / OpenJPA
  • Servlet-Api 3.1
  • JAX-RS 2.0.1
  • TomEE
  • Maven

Container?

TESTS?

  • Mocks
  • Arquillian
  • GlassFish Embedded
  • Embedded databases: H2, Derby

Results

  • Security error - [Ljava.net.URI; is not whitelisted as deserialisable, prevented before loading it.
  •     openJPA doesn’t support JPA 2.1 
  • 1 xml file
  • WPP: 2
  • GPP: 3

WPPs

ResultS

ResultS

public abstract class BaseDao<T extends BaseEntity> {
    @PersistenceContext(unitName = "enterpriseless-world")
    private EntityManager em;

    protected Class<T> entityClass;

    public BaseDao() {
        ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
        this.entityClass = 
           (Class) genericSuperclass.getActualTypeArguments()[0];
    }

    @TransactionAttribute
    public Long save(T entity) {
        em.persist(entity);
        return entity.getId();
    }

    public T find(Long id) {
        return em.find(entityClass, id);
    }

    @TransactionAttribute
    public void remove(T entity) {
        em.remove(entity);
    }

    protected EntityManager getEntityManager() {
        return em;
    }
}

Spring Framework

  • Forces to use Spring everywhere
  • Very close to be a new Java EE

BUT

Use Spring Boot

If you understand whole Spring Ecosystem

Spring Ecosystem

Alternatives

  • Play
  • Grails
  • Vert.x
  • Ratpack
  • Spark Framework
  • Lagom
  • No strict specifications
  • MVC only
  • Container-less or built-in containers
  • Convention over configuration approach

Play 2.x Framework

  • Non-blocking I/O (WebSockets included)
  • Built on top of Akka & Akka-streams
  • Built in testing tools
  • Built in ORM but you could choose
  • Hot redeploy
  • Convention over configuration
  • Container-less
  • Supports Java and Scala
  • Plugins as functionality vendors

Scala in action

  • Play Framework:
    • Netty (before 2.5) / Akka
    • Guice
    • SBT
    • Built-in Integration tests
  • Scalikejdbc

Results

  • No xml files were created
  • GPP 1 - "How to enable scalikejdbc?"
  • WPP 0

Simple controller

class ForeignExchangeController @Inject()(wsClient: WSClient,
                                          foreignExchangeService: ForeignExchangeService) extends Controller {
  
  def postLatestData() = Action.async {
    wsClient.url("http://api.fixer.io/latest").get().map { response =>
      response.status match {
        case play.api.http.Status.OK =>
          foreignExchangeService.batchPersist(getData(response.body))
          Ok("OK")
        case _ => InternalServerError
      }
    }
  }
  private def getData(content: String): Seq[(String, Double)] = {
    val pattern = "\"(\\S{3}?)\":(\\d+\\.?\\d*)".r
    pattern.findAllMatchIn(content).toSeq.collect {
      case a if (a.groupCount == 2) => (a.group(1), a.group(2).toDouble)
    }
  }

}
GET     /post   controllers.ForeignExchangeController.postLatestData()

Domain iteration

@Singleton
class CurrencyDao @Inject() (val config: Configuration) extends BaseDao {

  def batchPersist(data: Seq[(String, Double)]): Unit = DB autoCommit { implicit session =>
    val batchParams = data.map {d => Seq(d._1, d._2)}
    sql"insert into currencyScala(currency, created, rate) values(?, now(), ?)"
       .batch(batchParams: _*).apply()
  }

}
case class Currency(id: Option[Long], created: DateTime, currency: String, rate: Double)

Dao vs active record

object DB {
  val currencies = TableQuery[CurrencyTable]
}

DB.currencies.filter(_.currency === currency).sortBy(_.created.asc)
val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)

val currencies = TableQuery[CurrencyTable]

def add(currency: Currency): Future[String] = {
  dbConfig.db.run(currencies += user)
}

def delete(id: Long): Future[Int] = {
  dbConfig.db.run(currencies.filter(_.id === id).delete)
}

Conclusions

  • Use right tool to implement your business requirements
  • Do not hesitate to use something new
  • Be framework agnostic or use maximum light-weight frameworks
  • Keep it simple

Links

Frameworks battle

enterprise-less life

By Yegor Bondar

enterprise-less life

  • 1,057