Sunday, September 11, 2011

Mixing Scala and Java in a Project, Developed in Eclipse and Built by Maven

(Last updated on February 26, 2012)


 Overview

This is a tutorial showing how to setup a project (aka module) with both Scala and Java source code, to be built by Maven. In addition, this tutorial also shows how to setup the exact project in the Eclipse IDE so that developers can code and compile both Scala and Java source code in the same project.

If you only want to develop a mixed Java/Scala project in Eclipse, and you don't care about to build the project outside Eclipse using Maven and to manage Eclipse build path using Maven, you can do so using the Scala IDE for Eclipse out of box. 

Arguably, the largest advantage of the Java programming language over many other programming languages is the existence of countless libraries in Java.  Scala fully leverages this advantage of Java. Scala is very attractive for on one hand, it is a much more powerful programming language, and on the other hand, a Scala method can call almost any Java method as easy as another Java method does so. A Java method can also easily call a Scala method with certain limitations.

When develop a new project (as the term is used in the Eclipse IDE or the Maven build tool), it may be very desirable to have both Java and Scala classes in it. We may have some developers in the project who can only write Java code. We may also have to write some classes in Java in order to fit into certain frameworks or containers. Under those circumstances, a pure Scala project is not an option. If we still want to take advantage of the power of Scala, we have to mix Scala and Java in the same project. Being able to easily mix Scala and Java in the same project will greatly lower the obstacle for organizations to adopt Scala.

This tutorial is about the tricks in a Maven POM file to enable us to
  • Develop a project with both Scala and Java classes in Eclipse, where some Java methods call Scala methods, and some Scala methods call Java methods.
  • Use Maven to manage project build path in Eclipse
  • Build the project outside Eclipse using Maven
For a discussion on the advantages of using Maven to manage Eclipse project build path, see my other post.

Being able to build the project outside Eclipse using Maven makes it very easy to include that project into a larger project as a sub-project (called a module in Maven) later.

To follow this tutorial, you need:
  1. JDK 1.6 (not JDK 1.7)
  2. Eclipse Classic 3.6.x (not 3.7)
  3. The Scala IDE for Eclipse 2.0. The Scala IDE for Eclipse is an Eclipse plugin. If you need help in installing the plugin, please see the Appendix A at the end of this tutorial.
  4. Maven 2.2.1 or later
 In this tutorial, we develop a Hello World program consisting of:
  1. A Java class, GreetingInJava, with a method greet() which simply prints “Hello World!” to the console. It is normally the only class in a Java Hello World program.
  2. A Scala class GreetingInScala, with a method greet(), which instantiates a GreetingInJava instance, and call its greet() method. This is to show calling a Java method from a Scala method.
  3. A Java class, Bootstrap, with a main() method, in which a GreetingInScala instance is instantiated and the greet() method is called on that instance. This is to show calling a Scala method from a Java method.
Layout the Project

We are going to create the project outside Eclipse and to import it into Eclipse later. For more detailed about creating a project outside Eclipse and importing it into Eclipse, see my other post. Under C:\temp, create a directory structure as below.





Create Maven pom.xml

Create a pom.xml file under C:\temp\hello with the following content:



<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      http://maven.apache.org/maven-v4_0_0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>ted-gao</groupId>
    <artifactId>scala-java-mix</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>Scala-Java mixture</name>
    <description>Showcase mixing Scala and Java</description>
    <packaging>jar</packaging>
    
    <build>
        <plugins>
            <!-- ensure that we use JDK 1.6 -->
            <plugin>
                <inherited>true</inherited>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <version>2.15.2</version>
                <executions>
                    <!-- Run scala compiler in the process-resources phase, so that dependencies on
                         scala classes can be resolved later in the (Java) compile phase -->
                    <execution>
                        <id>scala-compile-first</id>
                        <phase>process-resources</phase>                        
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        </execution>

                        <!-- Run scala compiler in the process-test-resources phase, so that dependencies on
                             scala classes can be resolved later in the (Java) test-compile phase -->                    
                        <execution>
                        <id>scala-test-compile</id>
                        <phase>process-test-resources</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                    <!-- Add src/main/scala to source path of Eclipse -->
                    <execution>
                        <id>add-source</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>src/main/scala</source>
                            </sources>
                        </configuration>
                    </execution>
                      
                    <!-- Add src/test/scala to test source path of Eclipse -->
                    <execution>
                        <id>add-test-source</id>
                        <phase>generate-test-sources</phase>
                        <goals>
                            <goal>add-test-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>src/test/scala</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            
            <!-- to generate Eclipse artifacts for projects mixing Scala and Java -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.8</version>
                <configuration>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                    <projectnatures>
                        <projectnature>org.scala-ide.sdt.core.scalanature</projectnature>
                        <projectnature>org.eclipse.jdt.core.javanature</projectnature>
                    </projectnatures>
                    <buildcommands>
                        <buildcommand>org.scala-ide.sdt.core.scalabuilder</buildcommand>
                    </buildcommands>
                    <classpathContainers>
                        <classpathContainer>org.scala-ide.sdt.launching.SCALA_CONTAINER</classpathContainer>
                        <classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
                    </classpathContainers>
                    <excludes>
                        <!-- in Eclipse, use scala-library, scala-compiler from the SCALA_CONTAINER rather than POM <dependency> -->
                        <exclude>org.scala-lang:scala-library</exclude>
                        <exclude>org.scala-lang:scala-compiler</exclude>
                    </excludes>
                    <sourceIncludes>
                        <sourceInclude>**/*.scala</sourceInclude>
                        <sourceInclude>**/*.java</sourceInclude>
                    </sourceIncludes>
                </configuration>
            </plugin>
            
            <!-- When run tests in the test phase, include .java and .scala source files -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.8.1</version>
                <configuration>
                    <includes>
                        <include>**/*.java</include>
                        <include>**/*.scala</include>
                    </includes>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>2.9.0</version>
        </dependency>
    </dependencies>
</project>

It is OK for Java classes/interfaces to depend on Scala classes/objects because Scala classes/objects are compiled before Java classes/interfaces. It is also OK for Scala classes/objects/traits to depend Java classes/interfaces because Scala classes/objects/traits can be compiled against both Java source code and bytecode.


Create Eclipse Artifacts

Run Maven Eclipse plugin to create Eclipse project artifacts and configure the Eclipse workspace with path variable M2-REPO pointing to the local Maven repository. If you need help to do those tasks, please see my other post.

Create Java and Scala Classes

In Eclipse, create the following two Java classes and one Scala class.

GreetingInJava is a simple Java class.

package mix.java.scala;

package mix.java.scala;

public class GreetingInJava {
    public void greet() {
        System.out.println("Hello World!");
    }
}

GreetingInScala is a simple Scala class that calls Java.

package mix.java.scala

class GreetingInScala {
    def greet() {
        val delegate = new GreetingInJava
        delegate.greet()
    }
}

Bootstrap is a simple Java class that calls Scala. If you need help to create new Scala classes in Eclipse, see the Appendix B at the end of this tutorial.

package mix.java.scala;

public class Bootstrap {

    public static void main(String[] args) {
        GreetingInScala scala = new GreetingInScala();
        scala.greet();
    }
}

Run Bootstrap.java in Eclipse

Right click Bootstrap.java in the source editor in Eclipse. From the context menu, select Run As... -> Java Application.

You will see string "Hello World!" is printed to the console.

Build the Project Outside Eclipse, Using Maven

Open a command line window and cd to C:\temp\hello, execute the following command:

mvn install

You will see Maven compile the three Java and Scala classes and package them into a jar file.

Run Bootstrap.java outside Eclipse


Open a command line window and cd to C:\temp\hello, if you have not done so. Execute the following command:


mvn exec:java -Dexec.mainClass=mix.java.scala.Bootstrap

You will see "Hello World!" is printed to the command line window.


Conclusion


With the pow.xml in this tutorial, we will be able to create projects with Java and Scala source code mixed. We will be able to develop both Java and Scala source code in Eclipse. We will also be able to run the Java and Scala classes in Eclipse. We will also be able to build the project and to execute the classes outside Eclipse using Maven.

Appendix A – Installing the Scala IDE for Eclipse

On the Eclipse workbench, select Help -> Install New Software ...



When the Install dialog box appears, click the Add button. When the Add Repository dialog box appears, fill in a name for the plugin and the URL of the update site for the Scala IDE for Eclipse, and click the OK button.


Back to the Install dialog box, check all available plugins as shown on the snapshot below.


When you see the following warning, ignore it and click the OK button.


When the installation is finished, you need to restart Eclipse.

Appendix B – Creating New Scala Class in the Scala IDE for Eclipse

On the Package Explorer view, right click the desired package. From the context menu, select New -> Other… When the New dialog box appears, select Scala Wizards -> Scala Class.

[If you are also interested in developing Scala programs using Eclipse and sbt (instead of Maven) together, please read Using Eclipse and sbt in Scala Programming.]

5 comments:

oliver burkhalter said...

I had to change the line generate-test-sources to generate-sources in the "build-helper-maven-plugin" because somehow the Maven Eclipse Plugin didn't add the src/test/scala to the build path as test source directory in Eclpse IDE.

oliver burkhalter said...

Thanks for the article, was very helpful!

KannanDreams said...

Nice Article. Very Useful for me. Thanks for your effort and writeup.

Anonymous said...

Excellent tutorial, thank you!

Tanika Co Valda said...

Very much useful article. Kindly keep blogging

Java Training in Chennai

Java Online Training India