I've been evaluating Fantom vs. Scala for writing a simple server using the Netty library as a server socket framework.
I was having problems getting the Fantom version to work, so I tried stripping it down to a simple echo server. You fire up the server, telnet to port 8080, send a string, and it gets echoed back. I made Java, Scala, and Fantom implementations:
import org.jboss.netty.channel.SimpleChannelHandler
import org.jboss.netty.channel.ChannelHandlerContext
import org.jboss.netty.channel.MessageEvent
import org.jboss.netty.buffer.ChannelBuffer
import org.jboss.netty.channel.ExceptionEvent
class EchoHandler extends SimpleChannelHandler {
override def messageReceived(ctx: ChannelHandlerContext, e: MessageEvent) {
val channel = e.getChannel
val buffer = e.getMessage.asInstanceOf[ChannelBuffer]
val strBuffer = new StringBuilder
while(buffer.readable()) {
strBuffer.append( buffer.readByte.asInstanceOf[Char] )
}
val terms = strBuffer.toString
channel.write(terms + "\n")
channel.close
}
override def exceptionCaught(ctx: ChannelHandlerContext, e: ExceptionEvent) {
e.getCause.printStackTrace
e.getChannel.close
}
}
import java.net.InetSocketAddress
import java.util.concurrent.Executors
import org.jboss.netty.bootstrap.ServerBootstrap
import org.jboss.netty.channel.ChannelPipeline
import org.jboss.netty.channel.ChannelPipelineFactory
import org.jboss.netty.channel.Channels
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory
import org.jboss.netty.handler.codec.string.StringEncoder
import org.jboss.netty.util.CharsetUtil
object EchoServer {
def main(args: Array[String]): Unit = {
val socketFactory = new NioServerSocketChannelFactory(
Executors.newCachedThreadPool,
Executors.newCachedThreadPool
)
val bootstrap = new ServerBootstrap(socketFactory)
bootstrap.setPipelineFactory( new ChannelPipelineFactory {
def getPipeline = {
val pipeline = Channels.pipeline(new EchoHandler)
pipeline.addLast( "stringEncoder", new StringEncoder(CharsetUtil.UTF_8) )
pipeline
}
})
bootstrap.setOption("child.tcpNoDelay", true)
bootstrap.setOption("child.keepAlive", true)
bootstrap.bind( new InetSocketAddress(8080) )
}
}
Fantom:
using [java] org.jboss.netty.buffer
using [java] org.jboss.netty.channel
class ScrapeHandler : SimpleChannelHandler {
override Void messageReceived(ChannelHandlerContext? ctx, MessageEvent? e) {
buffer := (ChannelBuffer)e.getMessage();
terms := "";
while(buffer.readable) { terms += buffer.readByte }
echo(terms)
}
override Void exceptionCaught(ChannelHandlerContext? ctx, ExceptionEvent? e) {
e.getCause.printStackTrace
e.getChannel.close
}
}
using [java] org.jboss.netty.channel
using [java] org.jboss.netty.handler.codec.string
using [java] org.jboss.netty.util
using NettyTest::ScrapeHandler
class ScrapeChannelPipelineFactory : ChannelPipelineFactory {
override ChannelPipeline? getPipeline() {
handler := ScrapeHandler()
pipeline := Channels.pipeline()
pipeline.addFirst( "Scrape Handler", handler )
pipeline.addLast( "String Encoder", StringEncoder(CharsetUtil.UTF_8) )
return pipeline
}
}
using [java] java.net::InetSocketAddress
using [java] java.util.concurrent
using [java] org.jboss.netty.bootstrap
using [java] org.jboss.netty.channel
using [java] org.jboss.netty.channel.socket.nio
using [java] org.jboss.netty.handler.codec.string
using NettyTest::ScrapeChannelPipelineFactory
class Main {
static Void main() {
port := 8080
factory := NioServerSocketChannelFactory(
Executors.newCachedThreadPool,
Executors.newCachedThreadPool
)
bootstrap := ServerBootstrap(factory)
bootstrap.setPipelineFactory( ScrapeChannelPipelineFactory() )
bootstrap.setOption("child.tcpNoDelay", true)
bootstrap.setOption("child.keepAlive", true)
echo("Starting server on port $port")
bootstrap.bind( InetSocketAddress(port) )
}
}
The first question: Is there a way to write anonymous inner classes with Fantom? In the Fantom version I had to use three classes to accomplish this. I'm assuming that an idiomatic Fantom library would use a closure rather than an inner class, but are anonymous classes possible?
The second question is: Why doesn't the Fantom implementation work? I'm using Fantom 1.0.61 on OS X:
Fantom Launcher
Copyright (c) 2006-2011, Brian Frank and Andy Frank
Licensed under the Academic Free License version 3.0
Java Runtime:
java.version: 1.6.0_29
java.vm.name: Java HotSpot(TM) 64-Bit Server VM
java.vm.vendor: Apple Inc.
java.vm.version: 20.4-b02-402
java.home: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
fan.platform: macosx-x86_64
fan.version: 1.0.61
fan.env: sys::BootEnv
fan.home: /Users/bman/Applications/fantom-1.0.61
When I run the Java or Scala implementations, a server socket is bound on port 8080 and the program listens for requests. In the Fantom version, the program exits. I can't tell if the socket is never opened or if it's opened then immediately closed. In any case, I can't get the Netty library to work with Fantom. I've tried implementing the socket call using a native class and a native peer, neither of which works. My last attempt was to package the working Java implementation as a Jar and just call the server in one line from a Fantom class. That also doesn't work.
Is there something I'm missing or doing wrong? Is there some inherent incompatibility at play here? Any insight would be appreciated. I really like what I've seen of Fantom so far.
SlimerDudeSun 12 Feb 2012
Hiya,
From what I can gather, no, Fantom does not support inner classes, anonymous or otherwise. I believe it's just top level classes, much as everything in a Pod is lumped together in the one package.
If you're implementing a Java interface, and it has just one method then, yes, you can substitute a closure. i.e. java.lang.Runnable - See here: JavaFFI : Functions as Interfaces
Like yourself I'm new to Fantom, am still exploring, and therefore possibly wrong!
Steve.
brianSun 12 Feb 2012
When I run the Java or Scala implementations, a server socket is bound on port 8080 and the program listens for requests. In the Fantom version, the program exits. I can't tell if the socket is never opened or if it's opened then immediately closed.
I don't have Netty installed so can't test easily. In Fantom when you main exits it actually does a real force exit. So I suspect you just need to add an infinite sleep to the end of your program:
Actor.sleep(Duration.maxVal)
Fantom doesn't support inner classes largely for two reasons: 1) you often use closures and b) to keep type namespace simple and flat (to avoid overloads of the dot operator)
booleanmanSun 12 Feb 2012
Calling Actor.sleep(Duration.maxVal) did the trick. Also adding while (true) {} seems to work too. Thanks for the info!
brianSun 12 Feb 2012
By the way, you don't have to put all your classes into different source files like Java (you can put them all into one source file).
SlimerDudeSun 12 Feb 2012
Too true. I've recently found my self packaging similar / meaningful classes up into the same source file. Much as I would have made them inner classes in Java.
And this is fine, I can't ever think of a situation in Java where I've named 2 classes the same name and needed class resolution to distinguish between them.
dobesvTue 28 Feb 2012
Actually I think the reason they match the class name to the file name in Java is that you can easily find the file containing a certain class you're curious about. It's about easier code navigation.
However, IDEs do a really good job of indexing your code base these days so it doesn't really seem like that decision pays off. Plus programmers get good at searching around anyway.
This netty thing looks cool, I think it would be nice if Fantom used NIO to watch client connections instead of a thread per connection like it does now (for Weblets).
Using Netty with weblets might be one way to do that.
booleanman Sun 12 Feb 2012
I've been evaluating Fantom vs. Scala for writing a simple server using the Netty library as a server socket framework.
I was having problems getting the Fantom version to work, so I tried stripping it down to a simple echo server. You fire up the server, telnet to port 8080, send a string, and it gets echoed back. I made Java, Scala, and Fantom implementations:
Java
Scala:
Fantom:
The first question: Is there a way to write anonymous inner classes with Fantom? In the Fantom version I had to use three classes to accomplish this. I'm assuming that an idiomatic Fantom library would use a closure rather than an inner class, but are anonymous classes possible?
The second question is: Why doesn't the Fantom implementation work? I'm using Fantom 1.0.61 on OS X:
When I run the Java or Scala implementations, a server socket is bound on port 8080 and the program listens for requests. In the Fantom version, the program exits. I can't tell if the socket is never opened or if it's opened then immediately closed. In any case, I can't get the Netty library to work with Fantom. I've tried implementing the socket call using a native class and a native peer, neither of which works. My last attempt was to package the working Java implementation as a Jar and just call the server in one line from a Fantom class. That also doesn't work.
Is there something I'm missing or doing wrong? Is there some inherent incompatibility at play here? Any insight would be appreciated. I really like what I've seen of Fantom so far.
SlimerDude Sun 12 Feb 2012
Hiya,
From what I can gather, no, Fantom does not support inner classes, anonymous or otherwise. I believe it's just top level classes, much as everything in a Pod is lumped together in the one package.
If you're implementing a Java interface, and it has just one method then, yes, you can substitute a closure. i.e.
java.lang.Runnable
- See here: JavaFFI : Functions as InterfacesLike yourself I'm new to Fantom, am still exploring, and therefore possibly wrong!
Steve.
brian Sun 12 Feb 2012
I don't have Netty installed so can't test easily. In Fantom when you main exits it actually does a real force exit. So I suspect you just need to add an infinite sleep to the end of your program:
Fantom doesn't support inner classes largely for two reasons: 1) you often use closures and b) to keep type namespace simple and flat (to avoid overloads of the dot operator)
booleanman Sun 12 Feb 2012
Calling Actor.sleep(Duration.maxVal) did the trick. Also adding while (true) {} seems to work too. Thanks for the info!
brian Sun 12 Feb 2012
By the way, you don't have to put all your classes into different source files like Java (you can put them all into one source file).
SlimerDude Sun 12 Feb 2012
Too true. I've recently found my self packaging similar / meaningful classes up into the same source file. Much as I would have made them inner classes in Java.
And this is fine, I can't ever think of a situation in Java where I've named 2 classes the same name and needed class resolution to distinguish between them.
dobesv Tue 28 Feb 2012
Actually I think the reason they match the class name to the file name in Java is that you can easily find the file containing a certain class you're curious about. It's about easier code navigation.
However, IDEs do a really good job of indexing your code base these days so it doesn't really seem like that decision pays off. Plus programmers get good at searching around anyway.
This netty thing looks cool, I think it would be nice if Fantom used NIO to watch client connections instead of a thread per connection like it does now (for Weblets).
Using Netty with weblets might be one way to do that.