Skip to main content

Scala, JSF 2, and NetBeans

Posted by cayhorstmann on September 4, 2010 at 1:08 AM PDT

I am working on a web site that will help students practice their Scala programming skills. As I labored along, writing my JSF app code, I thought “this is silly—why not practice Scala at the same time?” But I like JSF and wasn't ready to jump to Lift or Vaadin.

With Eclipse, this isn't all that hard. Install the Java plugin. Make a dynamic web project in the usual way, using the Java EE perspective. Then, switch to the Scala perspective, right-click on the project, and, if all planets are aligned correctly, you will get a menu item “Add Scala nature”. (If they are not, see here for a manual approach.) Add your managed beans as Scala classes. Finally, switch back to the Java EE perspective, select the project properties, and add the Scala library JAR as a Java EE module dependency.

But I like NetBeans and wasn't ready to switch to Eclipse. (Unfortunately, JSF 2 support in Eclipse is pretty minimal, the Glassfish integration is a bit flaky, and the Scala plugin has very little usable code completion.)

NetBeans doesn't let me add a “Scala nature” to a web project. If I add Scala files to the project, I can edit them with the Scala editor, but they just get copied to the WAR file, without any compilation. I had one look at the Ant scripts for a Scala and a web project and decided that I wasn't going to figure out how to merge them.

This blog shows how you can use Maven to make a mixed Scala/Java project in NetBeans. So I gathered up JSF and Scala pom.xml files from here and here, cut out the considerable crud from the JSF POM file that was probably meant for supporting Tomcat, and merged the results to the best of my ability—see below.

You use the usual Maven directory structure, but with a src/main/scala directory instead of src/main/java:

This is the first example from Core JSF, but with a Scala managed bean:

@Named("user") @SessionScoped
class UserBean extends Serializable {
   @BeanProperty var name : String = ""
   @BeanProperty var password : String = ""
   def login = if (name != "") "welcome" else null
}

Now isn't that nicer than

@Named("user") @SessionScoped
public class UserBean implements Serializable {
    private String name = "";
    private String password = "";

    public String getName() { return name; }  
    public void setName(String newValue) { name = newValue; }

    public String getPassword() { return password; }
    public void setPassword(String newValue) { password = newValue; }  

    public String login() {
        if (!name.equals("")) {
            return "welcome"
        } else {
            return null;
        }
    }
}

Here is a zip file of the project. To test that it works, run

mvn package embedded-glassfish:run

Then point your browser to http://localhost:7070/scala-login, and the app should come up. Login with any non-blank user name, and you get this:

(Yes, it truly is JSF—the URL in the browser bar is one step behind...)

Now on to NetBeans. Select File -> New Project -> Maven -> Maven Project with Existing POM.

Just keep going in the wizard, and remember for next time that you could simply select File -> Open Project... instead.

What you get is pretty good—a project that you can build, deploy, and debug inside the IDE. Changes in the JSF pages are hot-deployed, but if you change your source code, you have to select the Debug -> Apply Code Changes menu option.

Also, you don't get autocompletion of CDI beans. Somehow NetBeans doesn't discover them from the deployed code. It does discover the message bundle for code completion, so maybe this is something they could fix in the future.

Here is the POM file:

<?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>com.horstmann.corejsf</groupId>
   <artifactId>scala-login</artifactId>
   <packaging>war</packaging>
   <version>1.0.0-SNAPSHOT</version>
  
   <properties>
      <scala.version>2.8.0</scala.version>
   </properties>
  
   <repositories>
      <repository>
         <id>scala-tools.org</id>
         <name>Scala-Tools Maven2 Repository</name>
         <url>http://scala-tools.org/repo-releases</url>
      </repository>
      <repository>
         <id>maven2-repository.dev.java.net</id>
         <name>Java.net Repository for Maven</name>
         <url>http://download.java.net/maven/2</url>
      </repository>
   </repositories>

   <pluginRepositories>
      <pluginRepository>
         <id>scala-tools.org</id>
         <name>Scala-Tools Maven2 Repository</name>
         <url>http://scala-tools.org/repo-releases</url>
      </pluginRepository>
      <pluginRepository>
         <id>glassfish</id>
         <name>GlassFish Maven 2 Repository</name>
         <url>http://download.java.net/maven/glassfish</url>
      </pluginRepository>
   </pluginRepositories>
  
   <dependencies>
      <dependency>
         <groupId>org.scala-lang</groupId>
         <artifactId>scala-library</artifactId>
         <version>${scala.version}</version>
      </dependency>
      <dependency>
         <groupId>javax</groupId>
         <artifactId>javaee-api</artifactId>
         <version>6.0</version>
      </dependency>
   </dependencies>

   <build>
      <sourceDirectory>src/main/scala</sourceDirectory>
      <finalName>${artifactId}</finalName>
      <plugins>
         <plugin>
            <groupId>org.scala-tools</groupId>
            <artifactId>maven-scala-plugin</artifactId>
            <executions>
               <execution>
                  <goals>
                     <goal>compile</goal>
                  </goals>
               </execution>
            </executions>
            <configuration>
               <scalaVersion>${scala.version}</scalaVersion>
            </configuration>
         </plugin>
        
         <!-- Compiler plugin enforces Java 1.6 compatibility -->
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
               <source>1.6</source>
               <target>1.6</target>
            </configuration>
         </plugin>

         <!-- Configure the Embedded GlassFish Maven plugin -->
         <plugin>
            <groupId>org.glassfish</groupId>
            <artifactId>maven-embedded-glassfish-plugin</artifactId>
            <version>3.0</version>
            <configuration>
               <app>${project.build.directory}/${build.finalName}.war</app>
               <port>7070</port>
               <containerType>web</containerType>
            </configuration>
         </plugin>
      </plugins>
   </build>

   <reporting>
      <plugins>
         <plugin>
            <groupId>org.scala-tools</groupId>
            <artifactId>maven-scala-plugin</artifactId>
            <configuration>
               <scalaVersion>${scala.version}</scalaVersion>
            </configuration>
         </plugin>
      </plugins>
   </reporting>

</project>

 

AttachmentSize
scala-login.zip5.78 KB
Related Topics >>

Comments

Scala, JSF 2, and NetBeans

Just to build off of your article, I've been looking around to see if there was a way to build a webapp without using Maven to get Scala to function in Netbeans 6.9.1 the work around I found was to create a separate Scala project that has a reference to the sevlet-api and then associate that Scala project with my web app in the libraries portion of the properties, I just had to make sure that I included the Scala jar files from the lib folder in my Scala distribution and that the Scala Servlet I wanted to use was mapped accordingly in my web.xml.
It's a little bit kludge, but it works for adding Scala classes to an existing web application.
Thanks

Nice!

Quite nice, Cay! Thanks for researching and presenting all the integration!

 Just in case someone asks

 Just in case someone asks "why bother", I just finished porting the Java code to Scala.

Java: 911 lines of code (27,565 characters)

Scala: 429 lines of code (13,761 characters)

Losing > 50% of the source code is pretty nice. Here is a typical example. I had to edit image tags and add a prefix to each image source.

Here is the Java code:

StringBuilder builder = new StringBuilder(statement);
Pattern pattern = Pattern.compile(&quot;[&lt;]\\s*[iI][mM][gG]\\s*[sS][rR][cC]\\s*[=]\\s*['\&quot;]([^'\&quot;]*)['\&quot;][^&gt;]*[&gt;]&quot;);
Matcher matcher = pattern.matcher(builder);
int from = 0;
while (matcher.find(from)) {
   int start = matcher.start(1);
   int end = matcher.end(1);
   builder.insert(start, prefix);
   from = end + prefix.length();
}
String description = builder.toString();

And here is the Scala equivalent:

val pattern = new Regex(&quot;([&lt;]\\s*[iI][mM][gG]\\s*[sS][rR][cC]\\s*[=]\\s*['\&quot;])([^'\&quot;]*['\&quot;][^&gt;]*[&gt;])&quot;)
val description = pattern.replaceAllIn(statement, m =&gt; m.group(1) + prefix + m.group(2))

RegEx

Pattern pattern = Pattern.compile("[<]\\s*[iI][mM][gG]\\s*[sS][rR][cC]\\s*[=]\\s*['\"]([^'\"]*)['\"][^>]*[>]");
If I am not mistaken, you use too much quote and you can simplify the expression, something like:
Pattern pattern = Pattern.compile("(?i)<\\s*img\\s*src\\s*=\\s*['\"]([^'\"]*)['\"][^>]*>");
(untested!)
Or perhaps even
Pattern pattern = Pattern.compile("(?i)<\\s*img\\s*src\\s*=\\s*(['\"])([^'\"]*)\\1[^>]*>");
to ensure having matching quotes... (need to use (2) in matcher.start/end then).

 Thanks! I didn't know

 Thanks! I didn't know that--very useful. Make it 902 lines of Java then :-) 

Try Project Lombok

Try project lombok (http://projectlombok.org/) on the java classes and then see the number of line... BTW how is the debug support for scala in NetBeans?

Debugger is pretty good

The debugger is pretty good. You can set breakpoints and view variables in the usual way. The only annoyance is that hot-swapping doesn't work as well as it should. The Maven script always recompiles everything, and then there is usually at least one class that makes the hot-swap fail. In contrast, when you work on a Java project, only the class that you changed is being swapped, and the chances of that swap being accepted are much better.

Lombok will eliminate the getters and setters, and maybe together with closures in Java--if and when they ever appear--one would come close to the LOC reduction that I saw with Scala. Getters and setters are certainly a large part of the code bloat.

 

 

In java ,you can write as

In java ,you can write as follows:
String regex = "([<]\\s*[iI][mM][gG]\\s*[sS][rR][cC]\\s*[=]\\s*['\"])([^'\"]*['\"][^>]*[>])";
String description = statement.replaceAll(regex,"$1"+prefix+"$2");
//maybe you should replace prefix with Matcher.quoteReplacement(prefix) if prefix contains '$' or '\'

This also works in scala.